documents/dev/snippets/aws/CloudFront DB other account.md
Table of Contents
Cloudfront Shared DB auth
On consumer account
Create an s3 bucket, cloudfront distribution (allow S3 to automatically update bucket policy), and lambda
In S3 bucket, under permissions, edit bucket policy Add the role associated with lambda, give it s3:GetObject access
Copy lambda code over Under configuration, permissions, click execution role name
Attach policies, add AmazonS3ReadOnlyAccess, AmazonDynamoDBReadOnlyAccess
Edit trust relationship, add edgelambda
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": [
"lambda.amazonaws.com",
"edgelambda.amazonaws.com"
]
},
"Action": "sts:AssumeRole"
}
]
}
Click on the default policy name (AWSLambdaBasicExecutionRole...), edit policy, go to JSON tab Add statement to allow consuming dynamo-reader role
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "logs:CreateLogGroup",
"Resource": "arn:aws:logs:us-east-1:432174144495:*"
},
{
"Effect": "Allow",
"Action": [
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": [
"arn:aws:logs:us-east-1:432174144495:log-group:/aws/lambda/cfViewerRequest:*"
]
},
{
"Effect": "Allow",
"Action": "sts:AssumeRole",
"Resource": "arn:aws:iam::997540067939:role/dynamo-reader"
}
]
}
Note the lambda execution role ARN
arn:aws:iam::432174144495:role/service-role/cfViewerRequest-role-21xgwbmi
On DesignTech AWS account
Edit dynamo-reader trust relationship, add the consumer role to trust t relationship https://console.aws.amazon.com/iam/home#/roles/dynamo-reader
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::432174144495:role/service-role/cfViewerRequest-role-21xgwbmi",
"Service": [
"lambda.amazonaws.com",
"edgelambda.amazonaws.com"
]
},
"Action": "sts:AssumeRole"
}
]
}
Lambda Edge
const AWS = require('aws-sdk');
const bcrypt = require('./bcrypt')
const TABLE_USER = 'partner_users';
const RoleArn = 'arn:aws:iam::997540067939:role/dynamo-reader';
let ddb;
const getDb = async ()=> {
if(typeof ddb !== 'undefined') return ddb;
// Create the STS service object
const sts = new AWS.STS({apiVersion: '2011-06-15'});
//Assume Role
const data = await sts.assumeRole({
RoleArn,
RoleSessionName: 'session1',
DurationSeconds: 900
}).promise();
// instantiate dynamo with credentials
ddb = new AWS.DynamoDB.DocumentClient({
apiVersion: '2012-08-10',
region: 'us-east-1',
sslEnabled: false,
paramValidation: false,
convertResponseTypes: false,
// role credentials
accessKeyId: data.Credentials.AccessKeyId,
secretAccessKey: data.Credentials.SecretAccessKey,
sessionToken: data.Credentials.SessionToken
});
return ddb;
};
const GetItemById = async (TableName, id)=> {
try {
const ddb = await getDb();
const data = await ddb.get({ TableName, Key: { id } }).promise();
return data.Item;
} catch (e) {
return null;
}
};
const getUser = async (id)=> {
return await GetItemById(TABLE_USER, id);
};
exports.handler = async(event, context) => {
// unauth response
let response = {
status: '401',
statusDescription: 'Unauthorized',
body: 'Unauthorized',
headers: {
'www-authenticate': [{ key: 'WWW-Authenticate', value: 'Basic' }]
},
};
// Get the request and its headers
const request = event.Records[0].cf.request;
const headers = request.headers;
// auto-fetch index.html
request.uri = request.uri.replace(/\/$/, '/index.html').replace(/\.net$/, '.net/index.html');
// Challenge for auth if auth credentials are absent
if(typeof headers.authorization === 'undefined') return response;
// decode auth key, check against db
const encodedStr = headers.authorization[0].value.substr(6);
const buffer = new Buffer(encodedStr, 'base64');
const decoded = buffer.toString('ascii').split(':');
const login = decoded[0];
const password = decoded[1];
// check login
let isAuthSuccessful = false;
let user = await getUser(login);
if(user) {
let {org, email, name, exp, pwd} = user;
let expDate = new Date(exp);
isAuthSuccessful = bcrypt.compareSync(password, pwd) && !!exp && (Date.now() < expDate);
}
if(!isAuthSuccessful) return response;
return request;
};
Lambda Example
const AWS = require('aws-sdk');
const bcrypt = require('./bcrypt')
const TABLE_USER = 'partner_users';
const TABLE_ORG = 'partner_orgs';
const RoleArn = 'arn:aws:iam::997540067939:role/dynamo-reader';
let ddb;
async function getDb() {
if(typeof ddb !== 'undefined') return ddb;
// Create the STS service object
const sts = new AWS.STS({apiVersion: '2011-06-15'});
//Assume Role
const data = await sts.assumeRole({
RoleArn,
RoleSessionName: 'session1',
DurationSeconds: 900
}).promise();
ddb = new AWS.DynamoDB.DocumentClient({
apiVersion: '2012-08-10',
region: 'us-east-1',
sslEnabled: false,
paramValidation: false,
convertResponseTypes: false,
// role credentials
accessKeyId: data.Credentials.AccessKeyId,
secretAccessKey: data.Credentials.SecretAccessKey,
sessionToken: data.Credentials.SessionToken
});
return ddb;
}
const GetItemById = async (TableName, id)=> {
try {
const ddb = await getDb();
const data = await ddb.get({ TableName, Key: { id } }).promise();
return data.Item;
} catch (e) {
return 'la';
}
};
const getUser = async (id)=> {
let user = await GetItemById(TABLE_USER, id);
return user;
};
exports.handler = async(event, context) => {
// unauth response
let response = {
statusCode: 401,
statusDescription: 'Unauthorized',
body: 'Unauthorized',
headers: {
'WWW-Authenticate': 'Basic'
},
};
// Get the request and its headers
const headers = event.headers;
// Challenge for auth if auth credentials are absent
if(typeof headers.authorization === 'undefined') return response;
// decode auth key, check against db
const encodedStr = headers.authorization.substr(6);
const buffer = new Buffer(encodedStr, 'base64');
const decoded = buffer.toString('ascii').split(':');
const login = decoded[0];
const password = decoded[1];
// check login
let isAuthSuccessful = false;
let user = await getUser(login);
if(user) {
let {org, email, name, exp, pwd, orgName, codename, policies} = user;
let expDate = new Date(user.exp);
isAuthSuccessful = bcrypt.compareSync(password, user.pwd) && !!user.exp && (Date.now() < expDate);
}
if(!isAuthSuccessful) return response;
return {
statusCode: 200,
body: JSON.stringify({message: 'success!', user}),
};
};