diff --git a/exports.js b/exports.js index 215cd9c338..140bd0e881 100644 --- a/exports.js +++ b/exports.js @@ -531,6 +531,7 @@ module.exports = { 'neptuneDBMultiAz' : require(__dirname + '/plugins/aws/neptune/neptuneDBMultiAz.js'), 'neptuneDbDeletionProtection' : require(__dirname + '/plugins/aws/neptune/neptuneDbDeletionProtection.js'), 'neptuneDBIamAuth' : require(__dirname + '/plugins/aws/neptune/neptuneDBIamAuth.js'), + 'neptuneAuditLoggingEnabled' : require(__dirname + '/plugins/aws/neptune/neptuneAuditLoggingEnabled.js'), 'monitoringMetrics' : require(__dirname + '/plugins/aws/cloudwatchlogs/monitoringMetrics.js'), diff --git a/plugins/aws/neptune/neptuneAuditLoggingEnabled.js b/plugins/aws/neptune/neptuneAuditLoggingEnabled.js new file mode 100644 index 0000000000..9307a6a1f5 --- /dev/null +++ b/plugins/aws/neptune/neptuneAuditLoggingEnabled.js @@ -0,0 +1,59 @@ +var async = require('async'); +var helpers = require('../../../helpers/aws'); + +module.exports = { + title: 'Neptune Audit Logging Enabled', + category: 'Neptune', + domain: 'Databases', + severity: 'Medium', + description: 'Ensure that audit logging is enabled for Neptune clusters to capture database activities, including login attempts, queries, and modifications.', + more_info: 'Enable that audit logging to capture database activities, including login attempts, queries, and modifications. Send the logs to Amazon CloudWatch or a centralized log management system for analysis and monitoring.', + recommended_action: 'Modify Neptune cluster and enable audit logging feature.', + link: 'https://docs.aws.amazon.com/neptune/latest/userguide/enable-cloudwatch-logs.html', + apis: ['Neptune:describeDBClusters'], + realtime_triggers: ['neptune:CreateDBCluster','neptune:ModifyDBCluster','neptune:DeleteDBCluster'], + + run: function(cache, settings, callback) { + var results = []; + var source = {}; + var regions = helpers.regions(settings); + + async.each(regions.neptune, function(region, rcb){ + var describeDBClusters = helpers.addSource(cache, source, + ['neptune', 'describeDBClusters', region]); + + if (!describeDBClusters) return rcb(); + + if (describeDBClusters.err || !describeDBClusters.data) { + helpers.addResult(results, 3, + `Unable to list Neptune database clusters: ${helpers.addError(describeDBClusters)}`, region); + return rcb(); + } + + if (!describeDBClusters.data.length) { + helpers.addResult(results, 0, + 'No Neptune database clusters found', region); + return rcb(); + } + + for (let cluster of describeDBClusters.data) { + if (!cluster.DBClusterArn || cluster.Engine !== 'neptune') continue; + + let resource = cluster.DBClusterArn; + + if (cluster.EnabledCloudwatchLogsExports && + cluster.EnabledCloudwatchLogsExports.length && + cluster.EnabledCloudwatchLogsExports.includes('audit')) { + helpers.addResult(results, 0, 'Neptune database cluster has audit logging enabled', region, resource); + } else { + helpers.addResult(results, 2, 'Neptune database cluster does not have audit logging enabled', region, resource); + } + } + + rcb(); + }, function(){ + callback(null, results, source); + }); + } +}; + diff --git a/plugins/aws/neptune/neptuneAuditLoggingEnabled.spec.js b/plugins/aws/neptune/neptuneAuditLoggingEnabled.spec.js new file mode 100644 index 0000000000..8b294b80bd --- /dev/null +++ b/plugins/aws/neptune/neptuneAuditLoggingEnabled.spec.js @@ -0,0 +1,132 @@ +var expect = require('chai').expect; +var neptuneAuditLoggingEnabled = require('./neptuneAuditLoggingEnabled'); + +const describeDBClusters = [ + { + AvailabilityZones: [], + BackupRetentionPeriod: 1, + DBClusterArn: 'arn:aws:rds:us-east-1:000011112222:cluster:neptune-cluster-1', + DBClusterIdentifier: 'neptune-cluster-1', + DBClusterParameterGroup: 'default.neptune1', + DBSubnetGroup: 'default-vpc-99de2fe4', + Status: 'available', + Engine: 'neptune', + EnabledCloudwatchLogsExports: [ "audit", "error"] + }, + { + AvailabilityZones: [], + BackupRetentionPeriod: 10, + DBClusterArn: 'arn:aws:rds:us-east-1:000011112223:cluster:neptune-cluster-2', + DBClusterIdentifier: 'neptune-cluster-2', + DBClusterParameterGroup: 'default.neptune1', + DBSubnetGroup: 'default-vpc-99de2fe4', + Status: 'available', + Engine: 'neptune', + EnabledCloudwatchLogsExports: [ "error"] + }, + { + AvailabilityZones: [], + BackupRetentionPeriod: 10, + DBClusterArn: 'arn:aws:rds:us-east-1:000011112224:cluster:neptune-cluster-3', + DBClusterIdentifier: 'neptune-cluster-3', + DBClusterParameterGroup: 'default.neptune1', + DBSubnetGroup: 'default-vpc-99de2fe4', + Status: 'available', + Engine: 'neptune', + EnabledCloudwatchLogsExports: [] + }, + { + AvailabilityZones: [], + BackupRetentionPeriod: 10, + DBClusterArn: 'arn:aws:rds:us-east-1:000011112225:cluster:neptune-cluster-4', + DBClusterIdentifier: 'neptune-cluster-4', + DBClusterParameterGroup: 'default.neptune1', + DBSubnetGroup: 'default-vpc-99de2fe4', + Status: 'available', + Engine: 'neptune' + } +]; + +const createCache = (clusters, clustersErr) => { + return { + neptune: { + describeDBClusters: { + 'us-east-1': { + err: clustersErr, + data: clusters + }, + }, + } + }; +}; + +describe('neptuneAuditLoggingEnabled', function () { + describe('run', function () { + it('should PASS if Neptune cluster has audit logging enabled', function (done) { + const cache = createCache([describeDBClusters[0]]); + neptuneAuditLoggingEnabled.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(0); + expect(results[0].message).to.include('Neptune database cluster has audit logging enabled'); + expect(results[0].region).to.equal('us-east-1'); + done(); + }); + }); + + it('should FAIL if Neptune cluster does not have audit logging enabled', function (done) { + const cache = createCache([describeDBClusters[1]]); + neptuneAuditLoggingEnabled.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(2); + expect(results[0].message).to.include('Neptune database cluster does not have audit logging enabled'); + expect(results[0].region).to.equal('us-east-1'); + done(); + }); + }); + + it('should FAIL if Neptune cluster has empty EnabledCloudwatchLogsExports', function (done) { + const cache = createCache([describeDBClusters[2]]); + neptuneAuditLoggingEnabled.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(2); + expect(results[0].message).to.include('Neptune database cluster does not have audit logging enabled'); + expect(results[0].region).to.equal('us-east-1'); + done(); + }); + }); + + it('should FAIL if Neptune cluster does not have EnabledCloudwatchLogsExports property', function (done) { + const cache = createCache([describeDBClusters[3]]); + neptuneAuditLoggingEnabled.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(2); + expect(results[0].message).to.include('Neptune database cluster does not have audit logging enabled'); + expect(results[0].region).to.equal('us-east-1'); + done(); + }); + }); + + it('should PASS if no Neptune clusters found', function (done) { + const cache = createCache([]); + neptuneAuditLoggingEnabled.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(0); + expect(results[0].message).to.include('No Neptune database clusters found'); + expect(results[0].region).to.equal('us-east-1'); + done(); + }); + }); + + it('should UNKNOWN if unable to list Neptune clusters', function (done) { + const cache = createCache(null, { message: "Unable to list Neptune clusters" }); + neptuneAuditLoggingEnabled.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(3); + expect(results[0].message).to.include('Unable to list Neptune database clusters:'); + expect(results[0].region).to.equal('us-east-1'); + done(); + }); + }); + }); +}); +