Skip to content

Instantly share code, notes, and snippets.

@cagataygurturk
Last active February 18, 2018 15:28
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 cagataygurturk/31bd893c76a4996db9919c9fafcdf993 to your computer and use it in GitHub Desktop.
Save cagataygurturk/31bd893c76a4996db9919c9fafcdf993 to your computer and use it in GitHub Desktop.
/**
* This lambda function creates the necessary security group for Cloudfront
*/
const Region = '${ResourceRegion}'; //Hardcoded on Cloudformation template
const VpcId = '${ResourceVpcId}'; //Hardcoded on Cloudformation template
const AWS = require('aws-sdk');
const GroupName = 'Cloudfront-SecurityGroup';
const topicArn = 'arn:aws:sns:us-east-1:806199016981:AmazonIpSpaceChanged';
const ipRangeFile = 'https://ip-ranges.amazonaws.com/ip-ranges.json';
const finish = {
send: (event, context, responseStatus, responseData, physicalResourceId) => {
if (typeof event.RequestType !== 'undefined') {
const cfnResponse = require("cfn-response");
return cfnResponse.send(event, context, responseStatus, responseData, physicalResourceId);
} else {
return context.done();
}
}
};
const response = {
SUCCESS: 'SUCCESS',
FAILED: 'FAILED'
};
const fetch = (url) => {
return new Promise((resolve, reject) => {
const lib = url.startsWith('https') ? require('https') : require('http');
const request = lib.get(url, (response) => {
if (response.statusCode < 200 || response.statusCode > 299) {
reject(new Error('Failed to load page, status code: ' + response.statusCode));
}
const body = [];
response.on('data', (chunk) => body.push(chunk));
response.on('end', () => resolve(body.join('')));
});
request.on('error', (err) => reject(err))
})
};
const addIngressRules = (ec2, groupId, ips, callback) => {
const ipRanges = [];
ips.map((ipAddress) => {
ipRanges.push({
CidrIp: ipAddress
});
});
ec2.authorizeSecurityGroupIngress({
GroupId: groupId,
IpPermissions: [
{
IpProtocol: '-1',
FromPort: '-1',
ToPort: '-1',
IpRanges: ipRanges
}]
})
.promise()
.then(() => {
return callback(null, {});
})
.catch((err) => {
return callback(err, null);
})
};
const allowIps = (ec2, groupId, callback) => {
fetch(ipRangeFile).then(response => {
let bodyAsJson = JSON.parse(response);
let ipsToAllow = bodyAsJson
.prefixes
.filter(prefix => prefix.service === 'CLOUDFRONT')
.map(prefix => prefix.ip_prefix);
addIngressRules(ec2, groupId, ipsToAllow, () => {
return callback(null, {});
});
}).catch((err) => {
return callback(err);
});
};
exports.handler = (event, context) => {
const RequestType = event.RequestType;
AWS.config.update({region: 'us-east-1'});
const sns = new AWS.SNS(); //SNS client must run on us-east-1 region
AWS.config.update({region: Region}); //The rest of AWS SDK clients must run on VPC's region.
sns.subscribe({
Protocol: 'lambda',
TopicArn: topicArn,
Endpoint: context.invokedFunctionArn
})
.promise()
.then(() => {
const ec2 = new AWS.EC2();
ec2.describeSecurityGroups({
Filters: [
{
Name: 'group-name',
Values: [
GroupName
]
}
]
})
.promise()
.then((data) => {
if (data.SecurityGroups.length === 0) { //Create the security group from scratch.
ec2.createSecurityGroup({
Description: 'Add this security group to a resource to make it accesible by Cloudfront',
GroupName: GroupName,
VpcId: VpcId
})
.promise()
.then((data) => {
allowIps(ec2, data.GroupId, (err, data) => {
if (err !== null) {
return finish.send(event, context, response.FAILED, err);
}
return finish.send(event, context, response.SUCCESS, null, data.GroupId);
});
})
.catch((err) => {
return finish.send(event, context, response.FAILED, err);
});
} else { //Just update the existing security group
const existingSecurityGroupId = data.SecurityGroups[0].GroupId;
if (RequestType === 'Delete') {
ec2.deleteSecurityGroup({
GroupId: existingSecurityGroupId
})
.promise()
.then(() => {
return finish.send(event, context, response.SUCCESS);
})
.catch((err) => {
return finish.send(event, context, response.FAILED, err);
});
} else {
const updateSecurityGroup = () => allowIps(ec2, existingSecurityGroupId, (err, data) => {
if (err !== null) {
return finish.send(event, context, response.FAILED, err);
}
return finish.send(event, context, response.SUCCESS, null, existingSecurityGroupId);
});
ec2
.revokeSecurityGroupIngress({
GroupId: existingSecurityGroupId,
IpPermissions: [
{
IpProtocol: '-1',
FromPort: '-1',
ToPort: '-1',
IpRanges: typeof data.SecurityGroups[0].IpPermissions[0] !== 'undefined' ? data.SecurityGroups[0].IpPermissions[0].IpRanges : []
}
]
})
.promise()
.then(() => {
updateSecurityGroup();
})
.catch(() => {
updateSecurityGroup();
});
}
}
}
);
})
.catch((err) => {
return finish.send(event, context, response.FAILED, err);
});
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment