Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,8 @@ The table below lists the supported auto auto-remediate functions:
| S3 | [S3-014](https://www.cloudconformity.com/conformity-rules/EC2/unrestricted-mongodb-access.html) | Ensure that your AWS S3 buckets are not publicly accessible via bucket policies in order to protect against unauthorized access. |
| TrustedAdvisor | [TrustedAdvisor-003](https://www.cloudconformity.com/conformity-rules/TrustedAdvisor/exposed-access-keys.html) | Ensure that there are not any exposed Amazon IAM access keys in order to protect your AWS resources against unapproved access |
| KMS | [KMS-002](https://www.cloudconformity.com/conformity-rules/KMS/key-rotation-enabled.html) | Ensure that the KMS Key Rotation is Enabled which allows you to set an yearly rotation schedule for your CMK |
| RedShift | [RS-019](https://www.cloudconformity.com/conformity-rules/Redshift/automated-snapshot-retention-period.html) | Ensure that the automated snapshot retention period set for your AWS Redshift clusters is a positive number, meaning that automated backups are enabled for the clusters |
| RedShift | [RS-019](https://www.cloudconformity.com/conformity-rules/Redshift/automated-snapshot-retention-period.html) | Ensure that the automated snapshot retention period set for your AWS Redshift clusters is a positive number, meaning that automated backups are enabled for the clusters
| EKS | [EKS-001](https://www.cloudconformity.com/knowledge-base/aws/EKS/endpoint-access.html) | Ensure that your Amazon EKS cluster's Kubernetes API server endpoint is not publicly accessible from the Internet in order to avoid exposing private data and minimizing security risks. |



Expand Down
41 changes: 41 additions & 0 deletions functions/AutoRemediateEKS-001.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
'use strict'
const AWS = require('aws-sdk')
/**
* Lambda function enforce EKS Endpoint not to be public
*
*/
module.exports.handler = (event, context, callback) => {
console.log('EKS Publicly Accessible Cluster Endpoints - Received event:', JSON.stringify(event, null, 2))
if (!event || !event.resource || !event.region) {
return handleError('Invalid event')
}

const clustername=event.resource.split('/')[1];

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
const clustername=event.resource.split('/')[1];
const resourceItems = event.resource.split('/');
if (resourceItems.length<1){
return handleError('Invalid resource');
}
const clustername=resourceItems[1];


let params = {
name: clustername,
resourcesVpcConfig: {
endpointPrivateAccess: true ,
endpointPublicAccess: false,
}
};

let eks = new AWS.EKS({region: event.region})

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
let eks = new AWS.EKS({region: event.region})
const eks = new AWS.EKS({region: event.region})


eks.updateClusterConfig(params, function(err, data) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggest to use asynchronous flow instead of using callbacks.
ref: https://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/using-promises.html

if (err) console.log(err, err.stack); // an error occurred
else console.log(data); // successful response
Comment on lines +26 to +27

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These if condition can be removed because line 29 handles it

Suggested change
if (err) console.log(err, err.stack); // an error occurred
else console.log(data); // successful response


if (err) {
console.log('Error', err)
return handleError(err.message ? err.message : 'Failed to modify cluster configuration')
}
return callback(null, 'Successfully processed event')

});

function handleError (message) {
message = message || 'Failed to process request.'
return callback(new Error(message))
}
}
76 changes: 60 additions & 16 deletions functions/AutoRemediateOrchestrator.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,29 @@

const CONFIG = require('./config')
const AWS = require('aws-sdk')
let delayedExecution;
let delay;
let queueURL;
let type;

module.exports.handler = (event, context, callback) => {
console.log('Received event: ', JSON.stringify(event, null, 2))
console.log('Config settings: ', JSON.stringify(CONFIG, null, 2))
//console.log('Config settings: ', JSON.stringify(CONFIG, null, 2))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove unused code

Suggested change
//console.log('Config settings: ', JSON.stringify(CONFIG, null, 2))


if (!event || !event.Records[0] || !event.Records[0].Sns || !event.Records[0].Sns.Message) {
if ((!event || !event.Records[0] || !event.Records[0].Sns || !event.Records[0].Sns.Message) && !event.Records[0].body) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can use middy for the input validation which will make it very tidy and readiable.

Suggested change
if ((!event || !event.Records[0] || !event.Records[0].Sns || !event.Records[0].Sns.Message) && !event.Records[0].body) {
if ((!event || !event.Records|| !event.Records[0] || !event.Records[0].Sns || !event.Records[0].Sns.Message) && !event.Records[0].body) {

callback(new Error('No event specified'))
return
}

let message = JSON.parse(event.Records[0].Sns.Message)
let message;
if(event.Records[0].body){

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is confuse because line 14 will return an error if event.Records[0].body is invalid. If this line of code runs, the else condition is redudant.

let sqsBody = JSON.parse(event.Records[0].body);
message = JSON.parse(sqsBody.Records[0].Sns.Message)
type=sqsBody.Records[0].Sns.Type
}
else{
message = JSON.parse(event.Records[0].Sns.Message)
type = event.Records[0].Sns.Type
}
let AutoRemediate = 'AutoRemediate' + message.ruleId

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use const and backticks

Suggested change
let AutoRemediate = 'AutoRemediate' + message.ruleId
const AutoRemediate = `AutoRemediate ${message.ruleId}`

ref: https://medium.com/better-programming/javascript-how-backticks-work-de269e0fb8ba


if (!CONFIG[`${AutoRemediate}`]) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (!CONFIG[`${AutoRemediate}`]) {
if (!CONFIG[AutoRemediate]) {

Expand All @@ -27,22 +39,54 @@ module.exports.handler = (event, context, callback) => {
return
}

let FunctionName =
if (!CONFIG[`${AutoRemediate}`]['delayedExecution']) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (!CONFIG[`${AutoRemediate}`]['delayedExecution']) {
if (!CONFIG[AutoRemediate]['delayedExecution']) {

console.log('Delayed execution disabled', AutoRemediate)
delayedExecution=false
}
else{
delayedExecution=true;
console.log('Delayed execution enabled', AutoRemediate)
delay=CONFIG[`${AutoRemediate}`]['delay']

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
delay=CONFIG[`${AutoRemediate}`]['delay']
delay=CONFIG[AutoRemediate]['delay']

queueURL='https://sqs.us-west-2.amazonaws.com/747218156759/CloudConformityAutoRemediate'

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We shouldn't leave the aws account in. We can use environment variable for queue url .
for example

queueURL= process.env.QUEUE_URL


}

//Event is configured as Delayed notification and this is the first time we're seeing the SNS message...
if (delayedExecution && type!="DelayedNotification") {
console.log("Delayed execution, sending event to SQS with Delay...")
event.Records[0].Sns.Type="DelayedNotification" //Change Type of message and put it on SQS with delay...
var sqs = new AWS.SQS();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
var sqs = new AWS.SQS();
const sqs = new AWS.SQS();

var params = {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
var params = {
const params = {

MessageBody: JSON.stringify(event, null, 2),
QueueUrl: queueURL,
DelaySeconds: delay,
}
sqs.sendMessage(params, function(err, data) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ consider to use asyn instead of callback

if (err) console.log(err, err.stack); // an error occurred
else console.log(data); // successful response
});
}
else{

//Compose the function name based on its own name...
let FunctionName =

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
let FunctionName =
const FunctionName =

process.env['AWS_LAMBDA_FUNCTION_NAME']
.substring(0, process.env['AWS_LAMBDA_FUNCTION_NAME'].lastIndexOf('-') + 1) +
AutoRemediate

console.log(`Invoking ${FunctionName} ...`)
console.log(`Invoking ${FunctionName} ...`)

let Lambda = new AWS.Lambda({region: process.env['AWS_REGION'], apiVersion: '2015-03-31'})
let Lambda = new AWS.Lambda({region: process.env['AWS_REGION'], apiVersion: '2015-03-31'})

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
let Lambda = new AWS.Lambda({region: process.env['AWS_REGION'], apiVersion: '2015-03-31'})
const Lambda = new AWS.Lambda({region: process.env['AWS_REGION'], apiVersion: '2015-03-31'})


Lambda.invoke({ FunctionName: `${FunctionName}`, Payload: JSON.stringify(message, null, 2) }, function (error, data) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Lambda.invoke({ FunctionName: `${FunctionName}`, Payload: JSON.stringify(message, null, 2) }, function (error, data) {
Lambda.invoke({ FunctionName, Payload: JSON.stringify(message, null, 2) }, function (error, data) {

if (error) {
console.log(`Error occurred while invoking ${FunctionName}`)
console.log(error)
callback(error)
} else {
callback(null, `Successfully invoked ${FunctionName} with result ${data}`)
}
})
}

Lambda.invoke({ FunctionName: `${FunctionName}`, Payload: JSON.stringify(message, null, 2) }, function (error, data) {
if (error) {
console.log(`Error occurred while invoking ${FunctionName}`)
console.log(error)
callback(error)
} else {
callback(null, `Successfully invoked ${FunctionName} with result ${data}`)
}
})
}
7 changes: 6 additions & 1 deletion functions/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,11 @@ module.exports = {
'enabled': false
},

'AutoRemediateEKS-001': {
'enabled': true,
'delayedExecution': true,
'delay' : 300
},
'AutoRemediateIAM-038': {
'enabled': false
},
Expand Down Expand Up @@ -179,7 +184,7 @@ module.exports = {
},

'AutoRemediateTrustedAdvisor-003': {
'enabled': false
'enabled': true
},

'AutoRemediateKMS-002': {
Expand Down
63 changes: 63 additions & 0 deletions serverless.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ service: auto-remediate

provider:
name: aws
versionFunctions: false
runtime: nodejs12.x
stage: v1
region: ${opt:region, 'us-east-1'}
Expand Down Expand Up @@ -635,6 +636,19 @@ functions:
- functions/AutoRemediateEC2-045.js
role: AutoRemediateEC2045Role

AutoRemediateEKS-001:
handler: functions/AutoRemediateEKS-001.handler
timeout: 120
memorySize: 128
tags:
Name: Auto Remediate EKS-001
Owner: CloudConformity
Role: Auto Remediate
Environment: Ops
package:
include:
- functions/AutoRemediateEKS-001.js
role: AutoRemediateEKS001Role
AutoRemediateEC2-038:
handler: functions/AutoRemediateEC2-038.handler
timeout: 120
Expand Down Expand Up @@ -708,6 +722,12 @@ resources:

Resources:

AutoRemediateQUEUE:
Type: AWS::SQS::Queue
Properties:
MessageRetentionPeriod: 7200 #2 Hours
QueueName: CloudConformityAutoRemediate

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The SQS queue must be encrypted at rest. Please configure KMS KeyId in the queue property

AutoRemediateEC2002Role:
Type: AWS::IAM::Role
Properties:
Expand Down Expand Up @@ -1568,6 +1588,14 @@ resources:
- Ref: 'AWS::Region'
- Ref: 'AWS::AccountId'
- 'log-group:/aws/lambda/*:*:*'
- Effect: Allow
Action:
- sqs:SendMessage
- sqs:ReceiveMessage
- sqs:DeleteMessage
- sqs:GetQueueAttributes
Resource:
Fn::GetAtt: [ AutoRemediateQUEUE, Arn ]
- Effect: Allow
Action:
- lambda:InvokeFunction
Expand Down Expand Up @@ -2044,6 +2072,41 @@ resources:
- ec2:RevokeSecurityGroupIngress
Resource: "*"

AutoRemediateEKS001Role:
Type: AWS::IAM::Role
Properties:
RoleName: AutoRemediateEKS-001Role
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
Action:
- sts:AssumeRole
Policies:
- PolicyName: AutoRemediateEKS001RolePolicy
PolicyDocument:
Statement:
- Effect: Allow
Action:
- logs:CreateLogGroup
- logs:CreateLogStream
- logs:PutLogEvents
Resource:
- 'Fn::Join':
- ':'
-
- 'arn:aws:logs'
- Ref: 'AWS::Region'
- Ref: 'AWS::AccountId'
- 'log-group:/aws/lambda/*:*:*'
- Effect: Allow
Action:
- eks:UpdateClusterConfig
Resource: "*"

AutoRemediateRS023Role:
Type: AWS::IAM::Role
Properties:
Expand Down
11 changes: 11 additions & 0 deletions test/AutoRemediateEKS-001.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
let event = {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
let event = {
const event = {

'resource': 'EKS-TEST',
'region': 'us-east-1'
}

let AutoRemediate = require('../functions/AutoRemediateEKS-001')

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
let AutoRemediate = require('../functions/AutoRemediateEKS-001')
const AutoRemediate = require('../functions/AutoRemediateEKS-001')


AutoRemediate.handler(event, {}, function (err, data) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add valid unit tests here.

console.log(err)
console.log('data', JSON.stringify(data, null, 2))
})