-
Notifications
You must be signed in to change notification settings - Fork 0
/
updateSecGroupRules.js
197 lines (175 loc) · 7.87 KB
/
updateSecGroupRules.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
/*
Read config files in folder "security-groups" with client IPs, and update chosen SGs.
*/
"use strict";
//var forEach = require('async-foreach').forEach;
var AWS = require('aws-sdk');
var async = require('async');
var ec2 = new AWS.EC2({ region: 'us-west-2', maxRetries: 5 , sslEnabled: true });
// load SG config files to sync all
var allSG = require('./security-groups/index.js');
// a sleep func to limit request rate to AWS
function sleep(time, callback) {
var stop = new Date().getTime();
while(new Date().getTime() < stop + time) {
;
}
callback();
}
// clear all ingress rules from SG, before applying new ones
function clearSG(oneSG, callback) {
var sgParams = {
DryRun: false,
GroupIds: [ oneSG.info.id ]
};
ec2.describeSecurityGroups(sgParams, function (err, data) {
if (err) {
console.log(err, err.stack);
callback(err);
} else {
// "data" has SecurityGroups[0] which is all info about this SG.
//console.log( data.SecurityGroups[0].IpPermissions);
// iterate through all "data.SecurityGroups[0].IpPermissions" array and revoke all rules.
// (to begin, assign object to temporary, and clean "UserIdGroupPairs" and "PrefixListIds"
// that breaks the "revokeSecurityGroupIngress" if used together with "cidr".
// But 'describe" returns them both)
var totalSets = data.SecurityGroups[0].IpPermissions.length
var totalSetsRevoked = 0
// if empty SG, callback now (run update now).
// in other cases update runs only after the last 'revoke' completed.
// to keep track if current callback is from 'last' revoke operation or there are more,
// i used simple counter "totalSetsRevoked" vs "totalSets"(array length)
if (totalSets === 0){
callback(null, totalSets, totalSetsRevoked);
} else {
// if some inbound rules found, iterate through their sets and when all cleared,
// callback and run updateSG. ("sets" are used and not single IPs because 'describeSecurityGroups'
// returns sets of CIDRs grouped by common ports. Those sets are inside 'IpPermissions')
data.SecurityGroups[0].IpPermissions.forEach(function (oneSetOfCIDRs) {
var modifiedIpPermissions = oneSetOfCIDRs;
//console.log("ONE SET: " + oneSetOfCIDRs + " of CIDRs");
// deleting the fields we not need (it breaks revoke function if passed "as is" from 'describe' func)
delete modifiedIpPermissions.UserIdGroupPairs;
delete modifiedIpPermissions.PrefixListIds;
var ruleParams = {
GroupId: oneSG.info.id,
IpPermissions: [modifiedIpPermissions]
};
ec2.revokeSecurityGroupIngress(ruleParams, function (err, data) {
if (err) {
console.log(" Error: " + err, err.stack);
callback(err);
}
else {
totalSetsRevoked++
console.log("[ " + oneSG.info.name + " ] - Deleted set " + totalSetsRevoked + " from " + totalSets);
callback(null, totalSets, totalSetsRevoked);
}
});
})
}
}
});
}
function updateSG(oneSG) {
var rulesNum = 0;
var params;
var newBunchOfIP;
var oldPortGroupTo = oneSG.rules[0].ToPort;
var portGroupTo = oneSG.rules[0].ToPort;
var oldPortGroupFrom = oneSG.rules[0].FromPort;
var portGroupFrom = oneSG.rules[0].FromPort;
var pushIP = "0.0.0.0";
oneSG.rules.forEach( function (oneRule) {
// create params only on 1st run with 1st CIDR, then only push more IPs or IP groups
portGroupTo = oneRule.ToPort;
portGroupFrom = oneRule.FromPort;
if ( (rulesNum == 0) && (oldPortGroupTo == portGroupTo) && (oldPortGroupFrom == portGroupFrom) ) {
// only first time, initialize the array "IpPermissions" and params. afterwards, add to it.
rulesNum++
params = {
DryRun: false,
GroupId: oneSG.info.id,
IpPermissions: [
{
FromPort: oneRule.FromPort,
IpProtocol: oneRule.Protocol,
ToPort: oneRule.ToPort,
IpRanges: [
{
CidrIp: oneRule.cidr
}
]
}
]
}
} else if ((oldPortGroupTo == portGroupTo) && (oldPortGroupFrom == portGroupFrom)) {
// for each next rule line, add to array, if it's same port group from and to.
pushIP = { CidrIp: oneRule.cidr };
// because we incremented rulesNum, point to 'previous bunch of IPs' here:
params.IpPermissions[rulesNum - 1].IpRanges.push(pushIP);
} else {
// if one of the ports changed, then it's a new "bunch" of settings
// that goes as object into array params.IpPermissions[]
newBunchOfIP = {
FromPort: oneRule.FromPort,
IpProtocol: oneRule.Protocol,
ToPort: oneRule.ToPort,
IpRanges: [
{
CidrIp: oneRule.cidr
}
]
};
params.IpPermissions[rulesNum] = newBunchOfIP;
rulesNum++;
// update current ports that are in use, to add more of the same to array
oldPortGroupTo = oneRule.ToPort;
oldPortGroupFrom = oneRule.FromPort;
}
// end rules foreach
});
// add the rules to SG, and show the number of successful rules added
ec2.authorizeSecurityGroupIngress(params, function (err, data) {
if (err) console.log(err, err.stack);
else console.log("[ " + oneSG.info.name + " ] auth rules packs success " + rulesNum); // success
});
}
// verify that no SG has more than 31 rules, otherwise exit (this is our limit in account, but you can change this).
for (var i in allSG){
if (allSG[i].rules.length > 31){
console.log("error: more than 31 inbound rules assigned to " + allSG[i].info.id);
console.log("please fix the error (create new file with more rules, don't pass 31) and retry");
process.exit()
} else {
console.log(allSG[i].info.name + " - has " + allSG[i].rules.length + " rules in file.")
}
}
// iterate array with all SGs, and clear rules + apply rules from files.
// allSG is array holding all chosen SGs like [sg1,sg2,sg3].
async.each(allSG, function(sg){
clearSG(sg, function (err, totalSets, totalSetsRevoked){
if (!err) {
// if clearing current rules - was successful,
// and total of revoked sets in SG === current revoked sets,
// then write new rules into SG.
if (totalSets === totalSetsRevoked){
sleep(1000, function() {
// executes after one second, and blocks the thread
updateSG(sg)
});
}
} else {
console.log(err);
}
} );
}, function(err){
// if any of the SG processing produced an error, err would equal that error
if( err ) {
// One of the iterations produced an error.
// All processing will now stop.
console.log('A SG failed to process');
} else {
console.log('All SG have been processed successfully');
}
});