From 12fce14ea5df065956261b5406fcbeea124da269 Mon Sep 17 00:00:00 2001 From: Geraldo Magella <44304595+gmagella-ca@users.noreply.github.com> Date: Wed, 8 Apr 2020 09:09:22 -0400 Subject: [PATCH 1/3] Including EKS-001 AutoRemediation --- functions/AutoRemediateEKS-001.js | 41 ++++++++++++++++++++++++++ functions/config.js | 3 ++ serverless.yml | 49 +++++++++++++++++++++++++++++++ test/AutoRemediateEKS-001.js | 11 +++++++ 4 files changed, 104 insertions(+) create mode 100644 functions/AutoRemediateEKS-001.js create mode 100644 test/AutoRemediateEKS-001.js diff --git a/functions/AutoRemediateEKS-001.js b/functions/AutoRemediateEKS-001.js new file mode 100644 index 0000000..60cd7ba --- /dev/null +++ b/functions/AutoRemediateEKS-001.js @@ -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') + } + + console.log('Info', event.resource) + + let params = { + name: event.resource, + resourcesVpcConfig: { + endpointPrivateAccess: true , + endpointPublicAccess: false, + } + }; + + let eks = new AWS.EKS({region: event.region}) + + eks.updateClusterConfig(params, function(err, data) { + 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)) + } +} diff --git a/functions/config.js b/functions/config.js index e9ec359..00cb101 100644 --- a/functions/config.js +++ b/functions/config.js @@ -117,6 +117,9 @@ module.exports = { 'enabled': false }, + 'AutoRemediateEKS-001': { + 'enabled': true + }, 'AutoRemediateIAM-038': { 'enabled': false }, diff --git a/serverless.yml b/serverless.yml index 4eb1316..c4b5293 100644 --- a/serverless.yml +++ b/serverless.yml @@ -2,6 +2,7 @@ service: auto-remediate provider: name: aws + versionFunctions: false runtime: nodejs12.x stage: v1 region: ${opt:region, 'us-east-1'} @@ -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 @@ -2044,6 +2058,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: diff --git a/test/AutoRemediateEKS-001.js b/test/AutoRemediateEKS-001.js new file mode 100644 index 0000000..eb988e2 --- /dev/null +++ b/test/AutoRemediateEKS-001.js @@ -0,0 +1,11 @@ +let event = { + 'resource': 'EKS-TEST', + 'region': 'us-east-1' +} + +let AutoRemediate = require('../functions/AutoRemediateEKS-001') + +AutoRemediate.handler(event, {}, function (err, data) { + console.log(err) + console.log('data', JSON.stringify(data, null, 2)) +}) From f4b6894945000426464cf70f8f507bd716f951f9 Mon Sep 17 00:00:00 2001 From: Geraldo Magella <44304595+gmagella-ca@users.noreply.github.com> Date: Wed, 8 Apr 2020 09:12:55 -0400 Subject: [PATCH 2/3] Adding check into README --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 81904e7..df25ce9 100644 --- a/README.md +++ b/README.md @@ -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. | From 78b8eef5b1676938639538cf69de610ec5377860 Mon Sep 17 00:00:00 2001 From: Geraldo Magella Date: Sun, 12 Apr 2020 22:03:08 -0400 Subject: [PATCH 3/3] adding delayed mechanism --- functions/AutoRemediateEKS-001.js | 4 +- functions/AutoRemediateOrchestrator.js | 76 ++++++++++++++++++++------ functions/config.js | 6 +- serverless.yml | 14 +++++ 4 files changed, 80 insertions(+), 20 deletions(-) diff --git a/functions/AutoRemediateEKS-001.js b/functions/AutoRemediateEKS-001.js index 60cd7ba..9060d2a 100644 --- a/functions/AutoRemediateEKS-001.js +++ b/functions/AutoRemediateEKS-001.js @@ -10,10 +10,10 @@ module.exports.handler = (event, context, callback) => { return handleError('Invalid event') } - console.log('Info', event.resource) + const clustername=event.resource.split('/')[1]; let params = { - name: event.resource, + name: clustername, resourcesVpcConfig: { endpointPrivateAccess: true , endpointPublicAccess: false, diff --git a/functions/AutoRemediateOrchestrator.js b/functions/AutoRemediateOrchestrator.js index 1458c67..868d964 100644 --- a/functions/AutoRemediateOrchestrator.js +++ b/functions/AutoRemediateOrchestrator.js @@ -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)) - 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) { callback(new Error('No event specified')) return } - - let message = JSON.parse(event.Records[0].Sns.Message) + let message; + if(event.Records[0].body){ + 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 if (!CONFIG[`${AutoRemediate}`]) { @@ -27,22 +39,54 @@ module.exports.handler = (event, context, callback) => { return } - let FunctionName = + 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'] + queueURL='https://sqs.us-west-2.amazonaws.com/747218156759/CloudConformityAutoRemediate' + + } + + //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(); + var params = { + MessageBody: JSON.stringify(event, null, 2), + QueueUrl: queueURL, + DelaySeconds: delay, + } + sqs.sendMessage(params, function(err, data) { + 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 = 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'}) + + 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}`) + } + }) + } - 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}`) - } - }) } diff --git a/functions/config.js b/functions/config.js index 00cb101..d583781 100644 --- a/functions/config.js +++ b/functions/config.js @@ -118,7 +118,9 @@ module.exports = { }, 'AutoRemediateEKS-001': { - 'enabled': true + 'enabled': true, + 'delayedExecution': true, + 'delay' : 300 }, 'AutoRemediateIAM-038': { 'enabled': false @@ -182,7 +184,7 @@ module.exports = { }, 'AutoRemediateTrustedAdvisor-003': { - 'enabled': false + 'enabled': true }, 'AutoRemediateKMS-002': { diff --git a/serverless.yml b/serverless.yml index c4b5293..4bd3c2a 100644 --- a/serverless.yml +++ b/serverless.yml @@ -722,6 +722,12 @@ resources: Resources: + AutoRemediateQUEUE: + Type: AWS::SQS::Queue + Properties: + MessageRetentionPeriod: 7200 #2 Hours + QueueName: CloudConformityAutoRemediate + AutoRemediateEC2002Role: Type: AWS::IAM::Role Properties: @@ -1582,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