Last active
February 18, 2018 15:28
-
-
Save cagataygurturk/31bd893c76a4996db9919c9fafcdf993 to your computer and use it in GitHub Desktop.
This file contains hidden or 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
/** | |
* 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