Skip to content

Commit eaa8a2a

Browse files
committed
Pushing the first runnable version
1 parent 9a898a3 commit eaa8a2a

5 files changed

Lines changed: 408 additions & 1 deletion

File tree

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ npm-debug.log*
55
yarn-debug.log*
66
yarn-error.log*
77
lerna-debug.log*
8-
8+
functions.csv
99
# Diagnostic reports (https://nodejs.org/api/report.html)
1010
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
1111

entrypoint.js

Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
#!/usr/bin/env node
2+
'use strict';
3+
4+
const path = require('path')
5+
const simplify = require('simplify-sdk')
6+
const provider = require('simplify-sdk/provider')
7+
const utilities = require('simplify-sdk/utilities')
8+
const CBEGIN = '\x1b[32m'
9+
const CERROR = '\x1b[31m'
10+
const CRESET = '\x1b[0m'
11+
const CDONE = '\x1b[37m'
12+
const opName = `SecOps`
13+
14+
var argv = require('yargs')
15+
.usage('simplify-secops status|patch|check|metric [options]')
16+
.string('input')
17+
.alias('i', 'input')
18+
.describe('input', 'Input file contains function list')
19+
.default('input', 'functions.csv')
20+
.string('hours')
21+
.describe('hours', 'How many hours since now eg: 12 - last 12 hours')
22+
.alias('h', 'hours')
23+
.string('periods')
24+
.describe('periods', 'Time resolution periods eg: 5 10 30 60 N*60 in seconds')
25+
.alias('t', 'periods')
26+
.string('profile')
27+
.describe('profile', 'AWS Profile configuration')
28+
.alias('p', 'profile')
29+
.default('profile', 'default')
30+
.string('region')
31+
.describe('region', 'AWS Specific Region')
32+
.alias('r', 'region')
33+
.default('region', 'eu-west-1')
34+
.demandOption(['i'])
35+
.demandCommand(1)
36+
.argv;
37+
38+
var configInputFile = argv.input || 'functions.csv'
39+
var scanOutput = {}
40+
var cmdOPS = (argv._[0] || 'status').toUpperCase()
41+
var lineIndex = 0
42+
var funcList = []
43+
44+
var files = require('fs').readFileSync(path.join(__dirname, configInputFile), 'utf-8').split(/\r?\n/)
45+
var headers = files[lineIndex++]
46+
47+
function analyseOrPatch(args) {
48+
const { functionInfo, triggerEvent, logRetention, customKmsArn, secureFunction, secureLog } = args
49+
return new Promise((resolve, reject) => {
50+
functionInfo.KMSKeyArn = functionInfo.KMSKeyArn || customKmsArn
51+
if (functionInfo.KMSKeyArn) {
52+
if (cmdOPS === 'PATCH') {
53+
let functionConfig = {
54+
FunctionName: functionInfo.FunctionName
55+
}
56+
if (secureFunction /** enabled */) {
57+
functionConfig.KMSKeyArn = functionInfo.KMSKeyArn
58+
}
59+
simplify.updateFunctionConfiguration({
60+
adaptor: provider.getFunction(),
61+
functionConfig: functionConfig
62+
}).then(functionOutput => {
63+
/** record new SHA256 Code Here */
64+
simplify.enableOrDisableLogEncryption({
65+
adaptor: provider.getKMS(),
66+
logger: provider.getLogger(),
67+
functionInfo: functionInfo,
68+
retentionInDays: logRetention,
69+
enableOrDisable: secureLog
70+
}).then(function (data) {
71+
console.log(`${CBEGIN}Simplify${CRESET} | \x1b[32m[DONE]\x1b[0m ${cmdOPS} ${functionInfo.FunctionName} : Configured secure logs with ${logRetention} days!`)
72+
resolve(args)
73+
}).catch(function (err) {
74+
reject(`${err}`)
75+
})
76+
}).catch(function (err) {
77+
reject(`${err}`)
78+
})
79+
} else if (cmdOPS === 'CHECK') {
80+
console.log(`${CBEGIN}Simplify${CRESET} | \x1b[32m[GOOD]\x1b[0m ${cmdOPS} ${functionInfo.FunctionName} : ${functionInfo.KMSKeyArn ? 'Has already configure with KMS Custom Key' : 'Provide a KMS Custom Key to apply a PATCH'}!`)
81+
resolve(args)
82+
} else {
83+
resolve(args)
84+
}
85+
} else {
86+
if (cmdOPS === 'PATCH') {
87+
if (secureFunction) {
88+
console.error(`${CBEGIN}Simplify${CRESET} | \x1b[31m[ERROR]\x1b[0m ${cmdOPS} ${functionInfo.FunctionName} : Provide a KMS Custom Key ARN!`)
89+
}
90+
resolve(args)
91+
} else if (cmdOPS === 'CHECK') {
92+
if (secureFunction) {
93+
console.log(`${CBEGIN}Simplify${CRESET} | \x1b[33m[WARN]\x1b[0m ${cmdOPS} ${functionInfo.FunctionName} : Provide a KMS Custom Key ARN!`)
94+
}
95+
resolve(args)
96+
} else {
97+
resolve(args)
98+
}
99+
}
100+
})
101+
}
102+
const secOpsFunctions = function (files, callback) {
103+
const currentLine = files[lineIndex++]
104+
if (currentLine) {
105+
const parts = currentLine.split(',')
106+
if (parts.length >= 7) {
107+
const functionArn = `arn:aws:lambda:${parts[0]}:${parts[1]}:function:${parts[2]}`
108+
const triggerEvent = parts[3] || 'Lambda'
109+
const logRetention = parts[4] || 90
110+
const customKmsArn = parts[5] ? `arn:aws:kms:${parts[0]}:${parts[1]}:key/${parts[5]}` : undefined
111+
const secureFunction = JSON.parse((parts[6] || 'false').toLowerCase())
112+
const secureLog = JSON.parse((parts[7] || 'false').toLowerCase())
113+
simplify.getFunctionConfiguration({
114+
adaptor: provider.getFunction(),
115+
functionConfig: { FunctionName: functionArn }
116+
}).then(function (functionInfo) {
117+
if (!scanOutput[functionInfo.FunctionName]) {
118+
scanOutput[functionInfo.FunctionName] = {}
119+
}
120+
scanOutput[functionInfo.FunctionName] = functionInfo
121+
analyseOrPatch({ functionInfo, triggerEvent, logRetention, customKmsArn, secureFunction, secureLog }).then(res => {
122+
funcList.push({ ...res })
123+
if (lineIndex >= files.length) {
124+
callback && callback(funcList)
125+
} else {
126+
secOpsFunctions(files, callback)
127+
}
128+
})
129+
}).catch(err => console.log(`${CBEGIN}Simplify${CRESET} | ERROR: ${err}`))
130+
}
131+
} else {
132+
callback && callback(funcList)
133+
}
134+
}
135+
136+
try {
137+
var config = simplify.getInputConfig({
138+
Region: argv.region || 'eu-west-1',
139+
Profile: argv.profile || 'default',
140+
Bucket: { Name: 'default' }
141+
})
142+
provider.setConfig(config).then(function () {
143+
if (headers.startsWith('Region')) {
144+
secOpsFunctions(files, function (list) {
145+
if (cmdOPS === 'METRIC') {
146+
let startDate = new Date()
147+
startDate.setHours(startDate.getHours() - (parseInt(argv.hours || 12)))
148+
simplify.getFunctionMetricData({
149+
adaptor: provider.getMetrics(),
150+
functions: list.map(l => { return { FunctionName: l.FunctionName } }),
151+
periods: parseInt(argv.periods || 300),
152+
startDate: startDate,
153+
endDate: new Date()
154+
}).then(metrics => {
155+
metrics.MetricDataResults.map(m => {
156+
let mData = []
157+
if (!m.Values.length) {
158+
mData.push({
159+
Label: m.Label,
160+
Timestamp: new Date().toISOString(),
161+
Value: '0'
162+
})
163+
} else {
164+
for (let i = 0; i < m.Values.length; i++) {
165+
mData.push({
166+
Label: m.Label,
167+
Timestamp: m.Timestamps[i],
168+
Value: m.Values[i]
169+
})
170+
}
171+
}
172+
utilities.printTableWithJSON(mData)
173+
})
174+
}).catch(err => console.error(`${err}`))
175+
} else {
176+
const outputTable = list.map(func => {
177+
return {
178+
FunctionName: func.functionInfo.FunctionName.truncateRight(20),
179+
LastModified: new Date(func.functionInfo.LastModified).toISOString(),
180+
State: func.functionInfo.State,
181+
CodeSize: func.functionInfo.CodeSize,
182+
Timeout: func.functionInfo.Timeout,
183+
CodeSha256: func.functionInfo.CodeSha256.truncateLeft(5),
184+
TriggerEvent: func.triggerEvent.truncateRight(12),
185+
LogRetention: func.logRetention,
186+
CustomKmsArn: (func.customKmsArn || '').truncateLeft(12),
187+
SecureFunction: func.secureFunction ? (func.functionInfo.KMSKeyArn ? 'OK' : 'YES - PATCH') : (func.functionInfo.KMSKeyArn ? 'NO - PATCH' : 'OK'),
188+
SecureLog: func.secureLog ? (func.functionInfo.KMSKeyArn ? 'OK' : 'YES - PATCH') : (func.functionInfo.KMSKeyArn ? 'NO - PATCH' : 'OK')
189+
}
190+
})
191+
utilities.printTableWithJSON(outputTable)
192+
}
193+
})
194+
}
195+
})
196+
} catch (err) {
197+
simplify.finishWithErrors(`${opName}-LoadConfig`, err)
198+
}

metrics.js

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
const AWS = require('aws-sdk')
2+
const path = require('path')
3+
4+
var argv = require('yargs')
5+
.usage('simplify-secops metrics [options]')
6+
.string('input')
7+
.alias('i', 'input')
8+
.describe('input', 'Input file contains function list')
9+
.default('input', 'functions.csv|deployment-input.json')
10+
.string('type')
11+
.describe('type', 'Type of input file [csv|openapi|graphql]')
12+
.alias('t', 'type')
13+
.default('type', 'csv')
14+
.string('profile')
15+
.describe('profile', 'AWS Profile configuration')
16+
.alias('p', 'profile')
17+
.default('profile', 'default')
18+
.string('region')
19+
.describe('region', 'AWS Specific Region')
20+
.alias('r', 'region')
21+
.default('region', 'eu-west-1')
22+
.demandOption(['i'])
23+
.demandCommand(1)
24+
.argv;
25+
26+
var cloudwatch = new AWS.CloudWatch({ apiVersion: '2010-08-01', region: argv.region });
27+
28+
function getLambdaMetrics(listFunc, metricName, startDate, endDate, periods) {
29+
return new Promise((resolve, reject) => {
30+
var params = {
31+
EndTime: endDate || new Date(),
32+
MetricName: metricName, /* Duration - Invocations - Throttles - Errors - ConcurrentExecutions */
33+
Namespace: 'AWS/Lambda', /* required */
34+
Period: periods || 10, /* 12 x (5 minutes) */
35+
StartTime: startDate,
36+
Dimensions: listFunc.map(func => {
37+
if (func.monitorMetrics) {
38+
return {
39+
Name: 'FunctionName',
40+
Value: `${func.FunctionName}`
41+
}
42+
}
43+
}).filter(func => func != undefined),
44+
Statistics: [
45+
"SampleCount",
46+
"Average",
47+
"Sum",
48+
"Minimum",
49+
"Maximum",
50+
/* more items */
51+
]
52+
};
53+
cloudwatch.getMetricStatistics(params, function (err, data) {
54+
err ? reject(err) : resolve(data)
55+
});
56+
})
57+
}

package-lock.json

Lines changed: 121 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)