diff --git a/exports.js b/exports.js index dcbf2a3dbb..93e22550dd 100644 --- a/exports.js +++ b/exports.js @@ -830,6 +830,7 @@ module.exports = { 'loadBalancerLoggingEnabled' : require(__dirname + '/plugins/azure/logalerts/loadBalancerLoggingEnabled.js'), 'virtualMachineLogging' : require(__dirname + '/plugins/azure/logalerts/virtualMachineLogging.js'), 'flexibleServerLoggingEnabled' : require(__dirname + '/plugins/azure/logalerts/flexibleServerLoggingEnabled.js'), + 'mysqlFlexibleServerLoggingEnabled': require(__dirname + '/plugins/azure/logalerts/mysqlFlexibleServerLoggingEnabled.js'), 'postgreSqlDBLoggingEnabled' : require(__dirname + '/plugins/azure/logalerts/postgreSqlDBLoggingEnabled.js'), 'sqlServerDatabaseRenameAlert' : require(__dirname + '/plugins/azure/logalerts/sqlServerDatabaseRenameAlert.js'), 'virtualMachinesPowerOffAlert' : require(__dirname + '/plugins/azure/logalerts/virtualMachinesPowerOffAlert.js'), diff --git a/plugins/azure/logalerts/mysqlFlexibleServerLoggingEnabled.js b/plugins/azure/logalerts/mysqlFlexibleServerLoggingEnabled.js new file mode 100644 index 0000000000..2d73fd945e --- /dev/null +++ b/plugins/azure/logalerts/mysqlFlexibleServerLoggingEnabled.js @@ -0,0 +1,34 @@ +var async = require('async'); +var helpers = require('../../../helpers/azure'); + +module.exports = { + title: 'MySQL Flexible Server Logging Enabled', + category: 'Log Alerts', + domain: 'Management and Governance', + severity: 'Medium', + description: 'Ensures Activity Log alerts for create/update and delete MySQL Flexible Server events are enabled.', + more_info: 'Monitoring for create/update and delete MySQL Flexible Server events gives insight into network access changes and may reduce the time it takes to detect suspicious activity.', + recommended_action: 'Add a new log alert to the Alerts service that monitors for MySQL Flexible Server create/update and delete events.', + link: 'https://learn.microsoft.com/en-us/azure/azure-monitor/platform/activity-log-alerts', + apis: ['activityLogAlerts:listBySubscriptionId'], + realtime_triggers: ['microsoftinsights:activitylogalerts:write', 'microsoftinsights:activitylogalerts:delete'], + + run: function(cache, settings, callback) { + var results = []; + var source = {}; + var locations = helpers.locations(settings.govcloud); + + async.each(locations.activityLogAlerts, function(location, rcb) { + var conditionResource = 'microsoft.dbformysql/flexibleservers'; + var text = 'MySQL Flexible Server'; + var activityLogAlerts = helpers.addSource(cache, source, + ['activityLogAlerts', 'listBySubscriptionId', location]); + + helpers.checkLogAlerts(activityLogAlerts, conditionResource, text, results, location); + + rcb(); + }, function() { + callback(null, results, source); + }); + } +}; diff --git a/plugins/azure/logalerts/mysqlFlexibleServerLoggingEnabled.spec.js b/plugins/azure/logalerts/mysqlFlexibleServerLoggingEnabled.spec.js new file mode 100644 index 0000000000..85b1f94552 --- /dev/null +++ b/plugins/azure/logalerts/mysqlFlexibleServerLoggingEnabled.spec.js @@ -0,0 +1,235 @@ +var expect = require("chai").expect; +var mysqlFlexibleServerLoggingEnabled = require("./mysqlFlexibleServerLoggingEnabled"); + +const activityLogAlerts = [ + { + "id": "/subscriptions/12345/resourceGroups/Default-ActivityLogAlerts/providers/microsoft.insights/activityLogAlerts/NSG2", + "name": "NSG2", + "type": "Microsoft.Insights/ActivityLogAlerts", + "location": "global", + "tags": {}, + "scopes": [ + "/subscriptions/123456" + ], + "enabled": true, + "condition": { + "allOf": [ + { + "field": "category", + "equals": "Security" + }, + { + "field": "operationName", + "equals": "Microsoft.DBforMySQL/flexibleservers/" + } + ] + }, + "actions": { + "actionGroups": [ + { + "actionGroupId": "/subscriptions/123456/resourcegroups/default-activitylogalerts/providers/microsoft.insights/actiongroups/testactiong", + "webhookProperties": {} + } + ] + } + }, + { + "id": "/subscriptions/123456/resourceGroups/Default-ActivityLogAlerts/providers/microsoft.insights/activityLogAlerts/NSG2", + "name": "NSG2", + "type": "Microsoft.Insights/ActivityLogAlerts", + "location": "global", + "tags": {}, + "scopes": [ + "/subscriptions/123456" + ], + "enabled": true, + "condition": { + "allOf": [ + { + "field": "category", + "equals": "Security" + }, + { + "field": "operationName", + "equals": "Microsoft.DBforMySQL/flexibleServers/write" + }, + { + "field": "operationName", + "equals": "Microsoft.DBforMySQL/flexibleServers/delete" + } + ] + }, + "actions": { + "actionGroups": [ + { + "actionGroupId": "/subscriptions/123456/resourcegroups/default-activitylogalerts/providers/microsoft.insights/actiongroups/testactiong", + "webhookProperties": {} + + } + ] + } + }, + { + "id": "/subscriptions/123456/resourceGroups/Default-ActivityLogAlerts/providers/microsoft.insights/activityLogAlerts/NSG2", + "name": "NSG2", + "type": "Microsoft.Insights/ActivityLogAlerts", + "location": "global", + "tags": {}, + "scopes": [ + "/subscriptions/123456" + ], + "enabled": true, + "condition": { + "allOf": [ + { + "field": "category", + "equals": "Security" + }, + { + "field": "operationName", + "equals": "Microsoft.DBforMySQL/flexibleServers/write" + } + ] + }, + "actions": { + "actionGroups": [ + { + "actionGroupId": "/subscriptions/123456/resourcegroups/default-activitylogalerts/providers/microsoft.insights/actiongroups/testactiong", + "webhookProperties": {} + } + ] + } + }, + { + "id": "/subscriptions/123456/resourceGroups/Default-ActivityLogAlerts/providers/microsoft.insights/activityLogAlerts/NSG2", + "name": "NSG2", + "type": "Microsoft.Insights/ActivityLogAlerts", + "location": "global", + "tags": {}, + "scopes": [ + "/subscriptions/123456" + ], + "enabled": true, + "condition": { + "allOf": [ + { + "field": "category", + "equals": "Security" + }, + { + "field": "operationName", + "equals": "Microsoft.DBforMySQL/flexibleServers/delete" + } + ] + }, + "actions": { + "actionGroups": [ + { + "actionGroupId": "/subscriptions/123456/resourcegroups/default-activitylogalerts/providers/microsoft.insights/actiongroups/testactiong", + "webhookProperties": {} + } + ] + } + } +]; + +const createCache = (err, data) => { + return { + activityLogAlerts: { + listBySubscriptionId: { + global: { + err: err, + data: data, + }, + }, + }, + }; +}; + +describe("mysqlFlexibleServerLoggingEnabled", function () { + describe("run", function () { + it("should give failing result if no activity log alerts found", function (done) { + const cache = createCache(null, []); + mysqlFlexibleServerLoggingEnabled.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(2); + expect(results[0].message).to.include("No existing Activity Alerts found"); + expect(results[0].region).to.equal("global"); + done(); + }); + }); + + it("should give unknown result if unable to query for Activity alerts", function (done) { + const cache = createCache(null); + mysqlFlexibleServerLoggingEnabled.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 query for Activity Alerts"); + expect(results[0].region).to.equal("global"); + done(); + }); + }); + + it("should give failing result if MySQL Flexible Server write and delete is not enabled", function (done) { + const cache = createCache(null, [activityLogAlerts[0]]); + mysqlFlexibleServerLoggingEnabled.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(2); + expect(results[0].message).to.include( + "Log Alert for MySQL Flexible Server write and delete is not enabled" + ); + expect(results[0].region).to.equal("global"); + done(); + }); + }); + + it("should give failing and passing results if MySQL Flexible Server delete is not enaled but write is enabled", function (done) { + const cache = createCache(null, [activityLogAlerts[2]]); + mysqlFlexibleServerLoggingEnabled.run(cache, {}, (err, results) => { + expect(results.length).to.equal(2); + expect(results[0].status).to.equal(0); + expect(results[0].message).to.include( + "Log alert for MySQL Flexible Server write is enabled" + ); + expect(results[0].region).to.equal("global"); + expect(results[1].status).to.equal(2); + expect(results[1].message).to.include( + "Log Alert for MySQL Flexible Server delete is not enabled" + ); + expect(results[0].region).to.equal("global"); + done(); + }); + }); + + it("should give failing and passing results if MySQL Flexible Server write is not enaled but delete is enabled", function (done) { + const cache = createCache(null, [activityLogAlerts[3]]); + mysqlFlexibleServerLoggingEnabled.run(cache, {}, (err, results) => { + expect(results.length).to.equal(2); + expect(results[0].status).to.equal(0); + expect(results[0].message).to.include( + "Log alert for MySQL Flexible Server delete is enabled" + ); + expect(results[0].region).to.equal("global"); + expect(results[1].status).to.equal(2); + expect(results[1].message).to.include( + "Log alert for MySQL Flexible Server write is not enabled" + ); + expect(results[0].region).to.equal("global"); + done(); + }); + }); + + it("should give passing result if MySQL Flexible Server Database write and delete is enabled", function (done) { + const cache = createCache(null, [activityLogAlerts[1]]); + mysqlFlexibleServerLoggingEnabled.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(0); + expect(results[0].message).to.include( + "Log Alert for MySQL Flexible Server write and delete is enabled" + ); + expect(results[0].region).to.equal("global"); + done(); + }); + }); + }); +});