mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
[Rule Migration] Add audit logging for SIEM Migration tasks. (#207831)
## Summary Adds audit logging for successful calls to these API routes: - Start/Stop migration - Create migration - Update Rule - Install Rule - Retrieve migration results. - Uploaded Macro/Lookup - Retrieved Macro/Lookup Tested it manually by going through the workflow with audit logging enabled: Enable: `xpack.security.audit.enabled: true` Results: ``` {"event":{"action":"siem_migration_created","category":["database"],"type":["creation"],"outcome":"success"},"user":{"name":"elastic","roles":["superuser"]},"kibana":{"space_id":"default"},"trace":{"id":"00885dd4-7fd9-45fe-9a0b-2173adcac4ad"},"client":{"ip":"127.0.0.1"},"service":{"node":{"roles":["background_tasks","ui"]}},"ecs":{"version":"8.11.0"},"@timestamp":"2025-01-22T15:05:12.875+01:00","message":"User created a new SIEM migration with [id=cd9552ce-05c8-4893-b659-b5a5ed9325d9","log":{"level":"INFO","logger":"plugins.security.audit.ecs"},"process":{"pid":91324,"uptime":540.063456227},"transaction":{"id":"ee4606116856393c"}} {"event":{"action":"siem_migration_started","category":["database"],"type":["start"],"outcome":"success"},"user":{"name":"elastic","roles":["superuser"]},"kibana":{"space_id":"default"},"trace":{"id":"e852b328-9e53-4c4d-b8ca-b8fa2b76383d"},"client":{"ip":"127.0.0.1"},"service":{"node":{"roles":["background_tasks","ui"]}},"ecs":{"version":"8.11.0"},"@timestamp":"2025-01-22T15:11:36.569+01:00","message":"User started an existing SIEM migration with [id=3805f79e-123c-4962-b22b-8ddf365cdd89]","log":{"level":"INFO","logger":"plugins.security.audit.ecs"},"process":{"pid":5438,"uptime":62.828177986},"transaction":{"id":"0ac652c8f722f1c4"}} {"event":{"action":"siem_migration_stopped","category":["database"],"type":["end"],"outcome":"success"},"user":{"name":"elastic","roles":["superuser"]},"kibana":{"space_id":"default"},"trace":{"id":"136f3a38-d47d-455d-bca2-aaf38559b20a"},"client":{"ip":"127.0.0.1"},"service":{"node":{"roles":["background_tasks","ui"]}},"ecs":{"version":"8.11.0"},"@timestamp":"2025-01-22T15:11:05.871+01:00","message":"User stopped an existing SIEM migration with [id=3805f79e-123c-4962-b22b-8ddf365cdd89]","log":{"level":"INFO","logger":"plugins.security.audit.ecs"},"process":{"pid":5438,"uptime":32.12840553},"transaction":{"id":"be379686654f4bdf"}} {"event":{"action":"siem_migration_updated_rule","category":["database"],"type":["change"],"outcome":"success"},"user":{"id":"u_mGBROF_q5bmFCATbLXAcCwKa0k8JvONAwSruelyKA5E_0","name":"elastic","roles":["superuser"]},"kibana":{"space_id":"default","session_id":"IPgQ3+R8DW9uxx4RQqUx9eZj+D5Es7SGQdcDoM/02l4="},"trace":{"id":"368b31e7-812d-464b-83d8-0e635c7fe5ed"},"client":{"ip":"127.0.0.1"},"service":{"node":{"roles":["background_tasks","ui"]}},"ecs":{"version":"8.11.0"},"@timestamp":"2025-01-22T15:13:15.827+01:00","message":"User updated a translated detection rule with [id=29pWjpQB_LGnD_bEV66u]","log":{"level":"INFO","logger":"plugins.security.audit.ecs"},"process":{"pid":5438,"uptime":162.09338085},"transaction":{"id":"404b3cb31be3c94f"}} {"event":{"action":"siem_migration_installed_rule","category":["database"],"type":["creation"],"outcome":"success"},"user":{"id":"u_mGBROF_q5bmFCATbLXAcCwKa0k8JvONAwSruelyKA5E_0","name":"elastic","roles":["superuser"]},"kibana":{"space_id":"default","session_id":"IPgQ3+R8DW9uxx4RQqUx9eZj+D5Es7SGQdcDoM/02l4="},"trace":{"id":"d90396dc-a0d3-4308-b07e-54761b562803"},"client":{"ip":"127.0.0.1"},"service":{"node":{"roles":["background_tasks","ui"]}},"ecs":{"version":"8.11.0"},"@timestamp":"2025-01-22T15:13:46.709+01:00","message":"User installed a new detection rule through SIEM migration with [id=3805f79e-123c-4962-b22b-8ddf365cdd89]","log":{"level":"INFO","logger":"plugins.security.audit.ecs"},"process":{"pid":5438,"uptime":192.977728866},"transaction":{"id":"4b17bafb9fbf48a4"}} {"event":{"action":"siem_migration_uploaded_macro","category":["database"],"type":["creation"],"outcome":"success"},"user":{"name":"elastic","roles":["superuser"]},"kibana":{"space_id":"default"},"trace":{"id":"e5fcb9cc-9d27-41cf-a171-13b9faf6078e"},"client":{"ip":"127.0.0.1"},"service":{"node":{"roles":["background_tasks","ui"]}},"ecs":{"version":"8.11.0"},"@timestamp":"2025-01-23T13:45:18.639+01:00","message":"User uploaded a new macro through SIEM migration with [id=2fc199ef-5bc7-4d87-a349-baeaea662273]","log":{"level":"INFO","logger":"plugins.security.audit.ecs"},"process":{"pid":89625,"uptime":106.911700803},"transaction":{"id":"fc95c131e86b0284"}} ```
This commit is contained in:
parent
aa4ce832f8
commit
06f3c30f60
9 changed files with 285 additions and 17 deletions
|
@ -8,15 +8,16 @@
|
|||
import type { IKibanaResponse, Logger } from '@kbn/core/server';
|
||||
import { buildRouteValidationWithZod } from '@kbn/zod-helpers';
|
||||
import { v4 as uuidV4 } from 'uuid';
|
||||
import { ResourceIdentifier } from '../../../../../common/siem_migrations/rules/resources';
|
||||
import { SIEM_RULE_MIGRATION_CREATE_PATH } from '../../../../../common/siem_migrations/constants';
|
||||
import {
|
||||
CreateRuleMigrationRequestBody,
|
||||
CreateRuleMigrationRequestParams,
|
||||
type CreateRuleMigrationResponse,
|
||||
} from '../../../../../common/siem_migrations/model/api/rules/rule_migration.gen';
|
||||
import { ResourceIdentifier } from '../../../../../common/siem_migrations/rules/resources';
|
||||
import type { SecuritySolutionPluginRouter } from '../../../../types';
|
||||
import type { CreateRuleMigrationInput } from '../data/rule_migrations_data_rules_client';
|
||||
import { SiemMigrationAuditLogger, SiemMigrationsAuditActions } from './util/audit';
|
||||
import { withLicense } from './util/with_license';
|
||||
|
||||
export const registerSiemRuleMigrationsCreateRoute = (
|
||||
|
@ -43,6 +44,7 @@ export const registerSiemRuleMigrationsCreateRoute = (
|
|||
async (context, req, res): Promise<IKibanaResponse<CreateRuleMigrationResponse>> => {
|
||||
const originalRules = req.body;
|
||||
const migrationId = req.params.migration_id ?? uuidV4();
|
||||
let siemMigrationAuditLogger: SiemMigrationAuditLogger | undefined;
|
||||
try {
|
||||
const [firstOriginalRule] = originalRules;
|
||||
if (!firstOriginalRule) {
|
||||
|
@ -51,7 +53,14 @@ export const registerSiemRuleMigrationsCreateRoute = (
|
|||
|
||||
const ctx = await context.resolve(['securitySolution']);
|
||||
const ruleMigrationsClient = ctx.securitySolution.getSiemRuleMigrationsClient();
|
||||
|
||||
const auditLogger = ctx.securitySolution.getAuditLogger();
|
||||
if (auditLogger) {
|
||||
siemMigrationAuditLogger = new SiemMigrationAuditLogger(auditLogger);
|
||||
}
|
||||
siemMigrationAuditLogger?.log({
|
||||
action: SiemMigrationsAuditActions.SIEM_MIGRATION_CREATED,
|
||||
id: migrationId,
|
||||
});
|
||||
const ruleMigrations = originalRules.map<CreateRuleMigrationInput>((originalRule) => ({
|
||||
migration_id: migrationId,
|
||||
original_rule: originalRule,
|
||||
|
@ -72,6 +81,11 @@ export const registerSiemRuleMigrationsCreateRoute = (
|
|||
return res.ok({ body: { migration_id: migrationId } });
|
||||
} catch (err) {
|
||||
logger.error(err);
|
||||
siemMigrationAuditLogger?.log({
|
||||
action: SiemMigrationsAuditActions.SIEM_MIGRATION_CREATED,
|
||||
error: err,
|
||||
id: migrationId,
|
||||
});
|
||||
return res.badRequest({ body: err.message });
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,14 +7,15 @@
|
|||
|
||||
import type { IKibanaResponse, Logger } from '@kbn/core/server';
|
||||
import { buildRouteValidationWithZod } from '@kbn/zod-helpers';
|
||||
import { SIEM_RULE_MIGRATION_PATH } from '../../../../../common/siem_migrations/constants';
|
||||
import {
|
||||
GetRuleMigrationRequestParams,
|
||||
GetRuleMigrationRequestQuery,
|
||||
type GetRuleMigrationResponse,
|
||||
} from '../../../../../common/siem_migrations/model/api/rules/rule_migration.gen';
|
||||
import { SIEM_RULE_MIGRATION_PATH } from '../../../../../common/siem_migrations/constants';
|
||||
import type { SecuritySolutionPluginRouter } from '../../../../types';
|
||||
import type { RuleMigrationGetOptions } from '../data/rule_migrations_data_rules_client';
|
||||
import { SiemMigrationAuditLogger, SiemMigrationsAuditActions } from './util/audit';
|
||||
import { withLicense } from './util/with_license';
|
||||
|
||||
export const registerSiemRuleMigrationsGetRoute = (
|
||||
|
@ -53,8 +54,13 @@ export const registerSiemRuleMigrationsGetRoute = (
|
|||
is_untranslatable: isUntranslatable,
|
||||
is_failed: isFailed,
|
||||
} = req.query;
|
||||
let siemMigrationAuditLogger: SiemMigrationAuditLogger | undefined;
|
||||
try {
|
||||
const ctx = await context.resolve(['securitySolution']);
|
||||
const auditLogger = ctx.securitySolution.getAuditLogger();
|
||||
if (auditLogger) {
|
||||
siemMigrationAuditLogger = new SiemMigrationAuditLogger(auditLogger);
|
||||
}
|
||||
const ruleMigrationsClient = ctx.securitySolution.getSiemRuleMigrationsClient();
|
||||
|
||||
const options: RuleMigrationGetOptions = {
|
||||
|
@ -75,9 +81,18 @@ export const registerSiemRuleMigrationsGetRoute = (
|
|||
|
||||
const result = await ruleMigrationsClient.data.rules.get(migrationId, options);
|
||||
|
||||
siemMigrationAuditLogger?.log({
|
||||
action: SiemMigrationsAuditActions.SIEM_MIGRATION_RETRIEVED,
|
||||
id: migrationId,
|
||||
});
|
||||
return res.ok({ body: result });
|
||||
} catch (err) {
|
||||
logger.error(err);
|
||||
siemMigrationAuditLogger?.log({
|
||||
action: SiemMigrationsAuditActions.SIEM_MIGRATION_RETRIEVED,
|
||||
error: err,
|
||||
id: migrationId,
|
||||
});
|
||||
return res.badRequest({ body: err.message });
|
||||
}
|
||||
})
|
||||
|
|
|
@ -14,8 +14,9 @@ import {
|
|||
InstallMigrationRulesRequestParams,
|
||||
} from '../../../../../common/siem_migrations/model/api/rules/rule_migration.gen';
|
||||
import type { SecuritySolutionPluginRouter } from '../../../../types';
|
||||
import { withLicense } from './util/with_license';
|
||||
import { SiemMigrationAuditLogger, SiemMigrationsAuditActions } from './util/audit';
|
||||
import { installTranslated } from './util/installation';
|
||||
import { withLicense } from './util/with_license';
|
||||
|
||||
export const registerSiemRuleMigrationsInstallRoute = (
|
||||
router: SecuritySolutionPluginRouter,
|
||||
|
@ -41,6 +42,7 @@ export const registerSiemRuleMigrationsInstallRoute = (
|
|||
async (context, req, res): Promise<IKibanaResponse<InstallMigrationRulesResponse>> => {
|
||||
const { migration_id: migrationId } = req.params;
|
||||
const { ids, enabled = false } = req.body;
|
||||
let siemMigrationAuditLogger: SiemMigrationAuditLogger | undefined;
|
||||
|
||||
try {
|
||||
const ctx = await context.resolve(['core', 'alerting', 'securitySolution']);
|
||||
|
@ -48,7 +50,14 @@ export const registerSiemRuleMigrationsInstallRoute = (
|
|||
const securitySolutionContext = ctx.securitySolution;
|
||||
const savedObjectsClient = ctx.core.savedObjects.client;
|
||||
const rulesClient = await ctx.alerting.getRulesClient();
|
||||
|
||||
const auditLogger = ctx.securitySolution.getAuditLogger();
|
||||
if (auditLogger) {
|
||||
siemMigrationAuditLogger = new SiemMigrationAuditLogger(auditLogger);
|
||||
}
|
||||
siemMigrationAuditLogger?.log({
|
||||
action: SiemMigrationsAuditActions.SIEM_MIGRATION_INSTALLED_RULE,
|
||||
id: migrationId,
|
||||
});
|
||||
const installed = await installTranslated({
|
||||
migrationId,
|
||||
ids,
|
||||
|
@ -61,6 +70,11 @@ export const registerSiemRuleMigrationsInstallRoute = (
|
|||
return res.ok({ body: { installed } });
|
||||
} catch (err) {
|
||||
logger.error(err);
|
||||
siemMigrationAuditLogger?.log({
|
||||
action: SiemMigrationsAuditActions.SIEM_MIGRATION_INSTALLED_RULE,
|
||||
error: err,
|
||||
id: migrationId,
|
||||
});
|
||||
return res.badRequest({ body: err.message });
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,13 +7,14 @@
|
|||
|
||||
import type { IKibanaResponse, Logger } from '@kbn/core/server';
|
||||
import { buildRouteValidationWithZod } from '@kbn/zod-helpers';
|
||||
import { SIEM_RULE_MIGRATION_RESOURCES_PATH } from '../../../../../../common/siem_migrations/constants';
|
||||
import {
|
||||
GetRuleMigrationResourcesRequestParams,
|
||||
GetRuleMigrationResourcesRequestQuery,
|
||||
type GetRuleMigrationResourcesResponse,
|
||||
} from '../../../../../../common/siem_migrations/model/api/rules/rule_migration.gen';
|
||||
import { SIEM_RULE_MIGRATION_RESOURCES_PATH } from '../../../../../../common/siem_migrations/constants';
|
||||
import type { SecuritySolutionPluginRouter } from '../../../../../types';
|
||||
import { SiemMigrationAuditLogger, SiemMigrationsAuditActions } from '../util/audit';
|
||||
import { withLicense } from '../util/with_license';
|
||||
|
||||
export const registerSiemRuleMigrationsResourceGetRoute = (
|
||||
|
@ -40,16 +41,47 @@ export const registerSiemRuleMigrationsResourceGetRoute = (
|
|||
async (context, req, res): Promise<IKibanaResponse<GetRuleMigrationResourcesResponse>> => {
|
||||
const migrationId = req.params.migration_id;
|
||||
const { type, names, from, size } = req.query;
|
||||
let siemMigrationAuditLogger: SiemMigrationAuditLogger | undefined;
|
||||
try {
|
||||
const ctx = await context.resolve(['securitySolution']);
|
||||
const ruleMigrationsClient = ctx.securitySolution.getSiemRuleMigrationsClient();
|
||||
|
||||
const auditLogger = ctx.securitySolution.getAuditLogger();
|
||||
if (auditLogger) {
|
||||
siemMigrationAuditLogger = new SiemMigrationAuditLogger(auditLogger);
|
||||
}
|
||||
const options = { filters: { type, names }, from, size };
|
||||
const resources = await ruleMigrationsClient.data.resources.get(migrationId, options);
|
||||
|
||||
if (type && type === 'macro') {
|
||||
siemMigrationAuditLogger?.log({
|
||||
action: SiemMigrationsAuditActions.SIEM_MIGRATION_RETRIEVED_MACRO,
|
||||
id: migrationId,
|
||||
});
|
||||
}
|
||||
if (type && type === 'lookup') {
|
||||
siemMigrationAuditLogger?.log({
|
||||
action: SiemMigrationsAuditActions.SIEM_MIGRATION_RETRIEVED_LOOKUP,
|
||||
id: migrationId,
|
||||
});
|
||||
}
|
||||
|
||||
return res.ok({ body: resources });
|
||||
} catch (err) {
|
||||
logger.error(err);
|
||||
if (type && type === 'macro') {
|
||||
siemMigrationAuditLogger?.log({
|
||||
action: SiemMigrationsAuditActions.SIEM_MIGRATION_RETRIEVED_MACRO,
|
||||
error: err,
|
||||
id: migrationId,
|
||||
});
|
||||
}
|
||||
if (type && type === 'lookup') {
|
||||
siemMigrationAuditLogger?.log({
|
||||
action: SiemMigrationsAuditActions.SIEM_MIGRATION_RETRIEVED_LOOKUP,
|
||||
error: err,
|
||||
id: migrationId,
|
||||
});
|
||||
}
|
||||
return res.badRequest({ body: err.message });
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,17 +8,18 @@
|
|||
import type { IKibanaResponse, Logger } from '@kbn/core/server';
|
||||
import { buildRouteValidationWithZod } from '@kbn/zod-helpers';
|
||||
import partition from 'lodash/partition';
|
||||
import { ResourceIdentifier } from '../../../../../../common/siem_migrations/rules/resources';
|
||||
import { SIEM_RULE_MIGRATION_RESOURCES_PATH } from '../../../../../../common/siem_migrations/constants';
|
||||
import {
|
||||
UpsertRuleMigrationResourcesRequestBody,
|
||||
UpsertRuleMigrationResourcesRequestParams,
|
||||
type UpsertRuleMigrationResourcesResponse,
|
||||
} from '../../../../../../common/siem_migrations/model/api/rules/rule_migration.gen';
|
||||
import { SIEM_RULE_MIGRATION_RESOURCES_PATH } from '../../../../../../common/siem_migrations/constants';
|
||||
import { ResourceIdentifier } from '../../../../../../common/siem_migrations/rules/resources';
|
||||
import type { SecuritySolutionPluginRouter } from '../../../../../types';
|
||||
import type { CreateRuleMigrationResourceInput } from '../../data/rule_migrations_data_resources_client';
|
||||
import { withLicense } from '../util/with_license';
|
||||
import { SiemMigrationAuditLogger, SiemMigrationsAuditActions } from '../util/audit';
|
||||
import { processLookups } from '../util/lookups';
|
||||
import { withLicense } from '../util/with_license';
|
||||
|
||||
export const registerSiemRuleMigrationsResourceUpsertRoute = (
|
||||
router: SecuritySolutionPluginRouter,
|
||||
|
@ -49,9 +50,29 @@ export const registerSiemRuleMigrationsResourceUpsertRoute = (
|
|||
): Promise<IKibanaResponse<UpsertRuleMigrationResourcesResponse>> => {
|
||||
const resources = req.body;
|
||||
const migrationId = req.params.migration_id;
|
||||
let siemMigrationAuditLogger: SiemMigrationAuditLogger | undefined;
|
||||
try {
|
||||
const ctx = await context.resolve(['securitySolution']);
|
||||
const ruleMigrationsClient = ctx.securitySolution.getSiemRuleMigrationsClient();
|
||||
const auditLogger = ctx.securitySolution.getAuditLogger();
|
||||
if (auditLogger) {
|
||||
siemMigrationAuditLogger = new SiemMigrationAuditLogger(auditLogger);
|
||||
}
|
||||
|
||||
for (const resource of resources) {
|
||||
if (resource.type === 'macro') {
|
||||
siemMigrationAuditLogger?.log({
|
||||
action: SiemMigrationsAuditActions.SIEM_MIGRATION_UPLOADED_MACRO,
|
||||
id: migrationId,
|
||||
});
|
||||
}
|
||||
if (resource.type === 'lookup') {
|
||||
siemMigrationAuditLogger?.log({
|
||||
action: SiemMigrationsAuditActions.SIEM_MIGRATION_UPLOADED_LOOKUP,
|
||||
id: migrationId,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the migration exists
|
||||
const { data } = await ruleMigrationsClient.data.rules.get(migrationId, { size: 1 });
|
||||
|
@ -83,6 +104,22 @@ export const registerSiemRuleMigrationsResourceUpsertRoute = (
|
|||
return res.ok({ body: { acknowledged: true } });
|
||||
} catch (err) {
|
||||
logger.error(err);
|
||||
for (const resource of resources) {
|
||||
if (resource.type === 'macro') {
|
||||
siemMigrationAuditLogger?.log({
|
||||
action: SiemMigrationsAuditActions.SIEM_MIGRATION_UPLOADED_MACRO,
|
||||
error: err,
|
||||
id: migrationId,
|
||||
});
|
||||
}
|
||||
if (resource.type === 'lookup') {
|
||||
siemMigrationAuditLogger?.log({
|
||||
action: SiemMigrationsAuditActions.SIEM_MIGRATION_UPLOADED_LOOKUP,
|
||||
error: err,
|
||||
id: migrationId,
|
||||
});
|
||||
}
|
||||
}
|
||||
return res.badRequest({ body: err.message });
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,8 +18,9 @@ import {
|
|||
type StartRuleMigrationResponse,
|
||||
} from '../../../../../common/siem_migrations/model/api/rules/rule_migration.gen';
|
||||
import type { SecuritySolutionPluginRouter } from '../../../../types';
|
||||
import { withLicense } from './util/with_license';
|
||||
import { SiemMigrationAuditLogger, SiemMigrationsAuditActions } from './util/audit';
|
||||
import { getRetryFilter } from './util/retry';
|
||||
import { withLicense } from './util/with_license';
|
||||
|
||||
export const registerSiemRuleMigrationsStartRoute = (
|
||||
router: SecuritySolutionPluginRouter,
|
||||
|
@ -49,12 +50,15 @@ export const registerSiemRuleMigrationsStartRoute = (
|
|||
connector_id: connectorId,
|
||||
retry,
|
||||
} = req.body;
|
||||
|
||||
let siemMigrationAuditLogger: SiemMigrationAuditLogger | undefined;
|
||||
try {
|
||||
const ctx = await context.resolve(['core', 'actions', 'alerting', 'securitySolution']);
|
||||
|
||||
const ruleMigrationsClient = ctx.securitySolution.getSiemRuleMigrationsClient();
|
||||
|
||||
const auditLogger = ctx.securitySolution.getAuditLogger();
|
||||
if (auditLogger) {
|
||||
siemMigrationAuditLogger = new SiemMigrationAuditLogger(auditLogger);
|
||||
}
|
||||
if (retry) {
|
||||
const { updated } = await ruleMigrationsClient.task.updateToRetry(
|
||||
migrationId,
|
||||
|
@ -76,9 +80,19 @@ export const registerSiemRuleMigrationsStartRoute = (
|
|||
if (!exists) {
|
||||
return res.noContent();
|
||||
}
|
||||
|
||||
siemMigrationAuditLogger?.log({
|
||||
action: SiemMigrationsAuditActions.SIEM_MIGRATION_STARTED,
|
||||
id: migrationId,
|
||||
});
|
||||
return res.ok({ body: { started } });
|
||||
} catch (err) {
|
||||
logger.error(err);
|
||||
siemMigrationAuditLogger?.log({
|
||||
action: SiemMigrationsAuditActions.SIEM_MIGRATION_STARTED,
|
||||
error: err,
|
||||
id: migrationId,
|
||||
});
|
||||
return res.badRequest({ body: err.message });
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,12 +7,13 @@
|
|||
|
||||
import type { IKibanaResponse, Logger } from '@kbn/core/server';
|
||||
import { buildRouteValidationWithZod } from '@kbn/zod-helpers';
|
||||
import { SIEM_RULE_MIGRATION_STOP_PATH } from '../../../../../common/siem_migrations/constants';
|
||||
import {
|
||||
StopRuleMigrationRequestParams,
|
||||
type StopRuleMigrationResponse,
|
||||
} from '../../../../../common/siem_migrations/model/api/rules/rule_migration.gen';
|
||||
import { SIEM_RULE_MIGRATION_STOP_PATH } from '../../../../../common/siem_migrations/constants';
|
||||
import type { SecuritySolutionPluginRouter } from '../../../../types';
|
||||
import { SiemMigrationAuditLogger, SiemMigrationsAuditActions } from './util/audit';
|
||||
import { withLicense } from './util/with_license';
|
||||
|
||||
export const registerSiemRuleMigrationsStopRoute = (
|
||||
|
@ -35,8 +36,13 @@ export const registerSiemRuleMigrationsStopRoute = (
|
|||
withLicense(
|
||||
async (context, req, res): Promise<IKibanaResponse<StopRuleMigrationResponse>> => {
|
||||
const migrationId = req.params.migration_id;
|
||||
let siemMigrationAuditLogger: SiemMigrationAuditLogger | undefined;
|
||||
try {
|
||||
const ctx = await context.resolve(['securitySolution']);
|
||||
const auditLogger = ctx.securitySolution.getAuditLogger();
|
||||
if (auditLogger) {
|
||||
siemMigrationAuditLogger = new SiemMigrationAuditLogger(auditLogger);
|
||||
}
|
||||
const ruleMigrationsClient = ctx.securitySolution.getSiemRuleMigrationsClient();
|
||||
|
||||
const { exists, stopped } = await ruleMigrationsClient.task.stop(migrationId);
|
||||
|
@ -44,9 +50,19 @@ export const registerSiemRuleMigrationsStopRoute = (
|
|||
if (!exists) {
|
||||
return res.noContent();
|
||||
}
|
||||
siemMigrationAuditLogger?.log({
|
||||
action: SiemMigrationsAuditActions.SIEM_MIGRATION_STOPPED,
|
||||
id: migrationId,
|
||||
});
|
||||
|
||||
return res.ok({ body: { stopped } });
|
||||
} catch (err) {
|
||||
logger.error(err);
|
||||
siemMigrationAuditLogger?.log({
|
||||
action: SiemMigrationsAuditActions.SIEM_MIGRATION_STOPPED,
|
||||
error: err,
|
||||
id: migrationId,
|
||||
});
|
||||
return res.badRequest({ body: err.message });
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,14 +7,15 @@
|
|||
|
||||
import type { IKibanaResponse, Logger } from '@kbn/core/server';
|
||||
import { buildRouteValidationWithZod } from '@kbn/zod-helpers';
|
||||
import { SIEM_RULE_MIGRATIONS_PATH } from '../../../../../common/siem_migrations/constants';
|
||||
import {
|
||||
UpdateRuleMigrationRequestBody,
|
||||
type UpdateRuleMigrationResponse,
|
||||
} from '../../../../../common/siem_migrations/model/api/rules/rule_migration.gen';
|
||||
import { SIEM_RULE_MIGRATIONS_PATH } from '../../../../../common/siem_migrations/constants';
|
||||
import type { SecuritySolutionPluginRouter } from '../../../../types';
|
||||
import { withLicense } from './util/with_license';
|
||||
import { SiemMigrationAuditLogger, SiemMigrationsAuditActions } from './util/audit';
|
||||
import { transformToInternalUpdateRuleMigrationData } from './util/update_rules';
|
||||
import { withLicense } from './util/with_license';
|
||||
|
||||
export const registerSiemRuleMigrationsUpdateRoute = (
|
||||
router: SecuritySolutionPluginRouter,
|
||||
|
@ -36,10 +37,20 @@ export const registerSiemRuleMigrationsUpdateRoute = (
|
|||
withLicense(
|
||||
async (context, req, res): Promise<IKibanaResponse<UpdateRuleMigrationResponse>> => {
|
||||
const rulesToUpdate = req.body;
|
||||
let siemMigrationAuditLogger: SiemMigrationAuditLogger | undefined;
|
||||
try {
|
||||
const ctx = await context.resolve(['securitySolution']);
|
||||
const ruleMigrationsClient = ctx.securitySolution.getSiemRuleMigrationsClient();
|
||||
|
||||
const auditLogger = ctx.securitySolution.getAuditLogger();
|
||||
if (auditLogger) {
|
||||
siemMigrationAuditLogger = new SiemMigrationAuditLogger(auditLogger);
|
||||
}
|
||||
for (const rule of rulesToUpdate) {
|
||||
siemMigrationAuditLogger?.log({
|
||||
action: SiemMigrationsAuditActions.SIEM_MIGRATION_UPDATED_RULE,
|
||||
id: rule.id,
|
||||
});
|
||||
}
|
||||
const transformedRuleToUpdate = rulesToUpdate.map(
|
||||
transformToInternalUpdateRuleMigrationData
|
||||
);
|
||||
|
@ -48,6 +59,13 @@ export const registerSiemRuleMigrationsUpdateRoute = (
|
|||
return res.ok({ body: { updated: true } });
|
||||
} catch (err) {
|
||||
logger.error(err);
|
||||
for (const rule of rulesToUpdate) {
|
||||
siemMigrationAuditLogger?.log({
|
||||
action: SiemMigrationsAuditActions.SIEM_MIGRATION_UPDATED_RULE,
|
||||
error: err,
|
||||
id: rule.id,
|
||||
});
|
||||
}
|
||||
return res.badRequest({ body: err.message });
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,108 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { AuditLogger, EcsEvent } from '@kbn/core/server';
|
||||
import type { ArrayElement } from '@kbn/utility-types';
|
||||
|
||||
export enum SiemMigrationsAuditActions {
|
||||
SIEM_MIGRATION_STARTED = 'siem_migration_started',
|
||||
SIEM_MIGRATION_STOPPED = 'siem_migration_stopped',
|
||||
SIEM_MIGRATION_CREATED = 'siem_migration_created',
|
||||
SIEM_MIGRATION_UPDATED = 'siem_migration_updated',
|
||||
SIEM_MIGRATION_RETRIEVED = 'siem_migration_retrieved',
|
||||
SIEM_MIGRATION_INSTALLED_RULE = 'siem_migration_installed_rule',
|
||||
SIEM_MIGRATION_UPDATED_RULE = 'siem_migration_updated_rule',
|
||||
SIEM_MIGRATION_UPLOADED_MACRO = 'siem_migration_uploaded_macro',
|
||||
SIEM_MIGRATION_RETRIEVED_MACRO = 'siem_migration_retrieved_macro',
|
||||
SIEM_MIGRATION_UPLOADED_LOOKUP = 'siem_migration_uploaded_lookup',
|
||||
SIEM_MIGRATION_RETRIEVED_LOOKUP = 'siem_migration_retrieved_lookup',
|
||||
}
|
||||
|
||||
export enum AUDIT_TYPE {
|
||||
CHANGE = 'change',
|
||||
START = 'start',
|
||||
END = 'end',
|
||||
ACCESS = 'access',
|
||||
CREATION = 'creation',
|
||||
}
|
||||
|
||||
export enum AUDIT_CATEGORY {
|
||||
API = 'api',
|
||||
AUTHENTICATION = 'authentication',
|
||||
DATABASE = 'database',
|
||||
WEB = 'web',
|
||||
}
|
||||
|
||||
export enum AUDIT_OUTCOME {
|
||||
FAILURE = 'failure',
|
||||
SUCCESS = 'success',
|
||||
UNKNOWN = 'unknown',
|
||||
}
|
||||
|
||||
export const siemMigrationAuditEventType: Record<
|
||||
SiemMigrationsAuditActions,
|
||||
ArrayElement<EcsEvent['type']>
|
||||
> = {
|
||||
siem_migration_started: AUDIT_TYPE.START,
|
||||
siem_migration_stopped: AUDIT_TYPE.END,
|
||||
siem_migration_created: AUDIT_TYPE.CREATION,
|
||||
siem_migration_updated: AUDIT_TYPE.CHANGE,
|
||||
siem_migration_retrieved: AUDIT_TYPE.ACCESS,
|
||||
siem_migration_installed_rule: AUDIT_TYPE.CREATION,
|
||||
siem_migration_updated_rule: AUDIT_TYPE.CHANGE,
|
||||
siem_migration_uploaded_macro: AUDIT_TYPE.CREATION,
|
||||
siem_migration_retrieved_macro: AUDIT_TYPE.ACCESS,
|
||||
siem_migration_uploaded_lookup: AUDIT_TYPE.CREATION,
|
||||
siem_migration_retrieved_lookup: AUDIT_TYPE.ACCESS,
|
||||
};
|
||||
|
||||
export const siemMigrationAuditEventMessage: Record<SiemMigrationsAuditActions, string> = {
|
||||
siem_migration_started: 'User started an existing SIEM migration',
|
||||
siem_migration_stopped: 'User stopped an existing SIEM migration',
|
||||
siem_migration_created: 'User created a new SIEM migration',
|
||||
siem_migration_updated: 'User updated an existing SIEM migration',
|
||||
siem_migration_retrieved: 'User retrieved rules from an existing SIEM migration',
|
||||
siem_migration_installed_rule: 'User installed a new detection rule through SIEM migration',
|
||||
siem_migration_updated_rule: 'User updated a translated detection rule',
|
||||
siem_migration_uploaded_macro: 'User uploaded a new macro through SIEM migration',
|
||||
siem_migration_retrieved_macro: 'User retrieved a SIEM migration macro',
|
||||
siem_migration_uploaded_lookup: 'User uploaded a new lookup list through SIEM migration',
|
||||
siem_migration_retrieved_lookup: 'User retrieved a SIEM migration lookup list',
|
||||
};
|
||||
|
||||
export interface SiemMigrationAuditEvent {
|
||||
action: SiemMigrationsAuditActions;
|
||||
error?: Error;
|
||||
id?: string;
|
||||
}
|
||||
|
||||
export class SiemMigrationAuditLogger {
|
||||
constructor(private readonly auditLogger: AuditLogger) {}
|
||||
|
||||
public log({ action, error, id }: SiemMigrationAuditEvent): void {
|
||||
const type = siemMigrationAuditEventType[action];
|
||||
let message = siemMigrationAuditEventMessage[action];
|
||||
|
||||
if (id) {
|
||||
message += ` with [id=${id}]`;
|
||||
}
|
||||
|
||||
this.auditLogger.log({
|
||||
message,
|
||||
event: {
|
||||
action,
|
||||
category: [AUDIT_CATEGORY.DATABASE],
|
||||
type: type ? [type] : undefined,
|
||||
outcome: error ? AUDIT_OUTCOME.FAILURE : AUDIT_OUTCOME.SUCCESS,
|
||||
},
|
||||
error: error && {
|
||||
code: error.name,
|
||||
message: error.message,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue