Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Security Solutions] Updates usage collector telemetry to use PIT (Point in Time) and restructuring of folders #124912

Merged
merged 19 commits into from
Feb 15, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
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
1 change: 0 additions & 1 deletion x-pack/plugins/security_solution/server/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,6 @@ export class Plugin implements ISecuritySolutionPlugin {

initUsageCollectors({
core,
kibanaIndex: core.savedObjects.getKibanaIndex(),
signalsIndex: DEFAULT_ALERTS_INDEX,
ml: plugins.ml,
usageCollection: plugins.usageCollection,
Expand Down
2 changes: 0 additions & 2 deletions x-pack/plugins/security_solution/server/usage/collector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ export interface UsageData {

export const registerCollector: RegisterCollector = ({
core,
kibanaIndex,
signalsIndex,
ml,
usageCollection,
Expand Down Expand Up @@ -516,7 +515,6 @@ export const registerCollector: RegisterCollector = ({
fetch: async ({ esClient }: CollectorFetchContext): Promise<UsageData> => {
const savedObjectsClient = await getInternalSavedObjectsClient(core);
const detectionMetrics = await getDetectionsMetrics({
kibanaIndex,
signalsIndex,
esClient,
savedObjectsClient,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,11 @@ import {
getMockMlDatafeedStatsResponse,
getMockRuleSearchResponse,
} from './ml_jobs/get_metrics.mocks';
import { getMockRuleAlertsResponse, getMockAlertCasesResponse } from './rules/get_metrics.mocks';
import {
getMockRuleAlertsResponse,
getMockAlertCaseCommentsResponse,
getEmptySavedObjectResponse,
} from './rules/get_metrics.mocks';
import { getInitialDetectionMetrics } from './get_initial_usage';
import { getDetectionsMetrics } from './get_metrics';
import { getInitialRulesUsage } from './rules/get_initial_usage';
Expand All @@ -41,7 +45,6 @@ describe('Detections Usage and Metrics', () => {
it('returns zeroed counts if calls are empty', async () => {
const logger = loggingSystemMock.createLogger();
const result = await getDetectionsMetrics({
kibanaIndex: '',
signalsIndex: '',
esClient,
savedObjectsClient,
Expand All @@ -52,13 +55,13 @@ describe('Detections Usage and Metrics', () => {
});

it('returns information with rule, alerts and cases', async () => {
esClient.search
.mockResponseOnce(getMockRuleSearchResponse())
.mockResponseOnce(getMockRuleAlertsResponse(3400));
savedObjectsClient.find.mockResolvedValue(getMockAlertCasesResponse());
esClient.search.mockResponseOnce(getMockRuleAlertsResponse(3400));
savedObjectsClient.find.mockResolvedValueOnce(getMockRuleSearchResponse());
savedObjectsClient.find.mockResolvedValueOnce(getMockAlertCaseCommentsResponse());
// Get empty saved object for legacy notification system.
savedObjectsClient.find.mockResolvedValueOnce(getEmptySavedObjectResponse());
const logger = loggingSystemMock.createLogger();
const result = await getDetectionsMetrics({
kibanaIndex: '',
signalsIndex: '',
esClient,
savedObjectsClient,
Expand Down Expand Up @@ -113,13 +116,13 @@ describe('Detections Usage and Metrics', () => {
});

it('returns information with on non elastic prebuilt rule', async () => {
esClient.search
.mockResponseOnce(getMockRuleSearchResponse('not_immutable'))
.mockResponseOnce(getMockRuleAlertsResponse(800));
savedObjectsClient.find.mockResolvedValue(getMockAlertCasesResponse());
esClient.search.mockResponseOnce(getMockRuleAlertsResponse(800));
savedObjectsClient.find.mockResolvedValueOnce(getMockRuleSearchResponse('not_immutable'));
savedObjectsClient.find.mockResolvedValueOnce(getMockAlertCaseCommentsResponse());
// Get empty saved object for legacy notification system.
savedObjectsClient.find.mockResolvedValueOnce(getEmptySavedObjectResponse());
const logger = loggingSystemMock.createLogger();
const result = await getDetectionsMetrics({
kibanaIndex: '',
signalsIndex: '',
esClient,
savedObjectsClient,
Expand Down Expand Up @@ -159,13 +162,14 @@ describe('Detections Usage and Metrics', () => {
});

it('returns information with rule, no alerts and no cases', async () => {
esClient.search
.mockResponseOnce(getMockRuleSearchResponse())
.mockResponseOnce(getMockRuleAlertsResponse(0));
savedObjectsClient.find.mockResolvedValue(getMockAlertCasesResponse());
esClient.search.mockResponseOnce(getMockRuleAlertsResponse(0));
savedObjectsClient.find.mockResolvedValueOnce(getMockRuleSearchResponse());
savedObjectsClient.find.mockResolvedValueOnce(getMockAlertCaseCommentsResponse());
// Get empty saved object for legacy notification system.
savedObjectsClient.find.mockResolvedValueOnce(getEmptySavedObjectResponse());

const logger = loggingSystemMock.createLogger();
const result = await getDetectionsMetrics({
kibanaIndex: '',
signalsIndex: '',
esClient,
savedObjectsClient,
Expand Down Expand Up @@ -225,6 +229,7 @@ describe('Detections Usage and Metrics', () => {
esClient = elasticsearchServiceMock.createClusterClient().asInternalUser;
mlClient = mlServicesMock.createSetupContract();
savedObjectsClient = savedObjectsClientMock.create();
savedObjectsClient.find.mockResolvedValue(getEmptySavedObjectResponse());
});

it('returns an empty array if there is no data', async () => {
Expand All @@ -234,7 +239,6 @@ describe('Detections Usage and Metrics', () => {
} as unknown as ReturnType<typeof mlClient.anomalyDetectorsProvider>);
const logger = loggingSystemMock.createLogger();
const result = await getDetectionsMetrics({
kibanaIndex: '',
signalsIndex: '',
esClient,
savedObjectsClient,
Expand Down Expand Up @@ -267,7 +271,6 @@ describe('Detections Usage and Metrics', () => {
} as unknown as ReturnType<typeof mlClient.anomalyDetectorsProvider>);

const result = await getDetectionsMetrics({
kibanaIndex: '',
signalsIndex: '',
esClient,
savedObjectsClient,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import { getInitialRulesUsage } from './rules/get_initial_usage';
import { getInitialMlJobUsage } from './ml_jobs/get_initial_usage';

export interface GetDetectionsMetricsOptions {
kibanaIndex: string;
signalsIndex: string;
esClient: ElasticsearchClient;
savedObjectsClient: SavedObjectsClientContract;
Expand All @@ -24,7 +23,6 @@ export interface GetDetectionsMetricsOptions {
}

export const getDetectionsMetrics = async ({
kibanaIndex,
signalsIndex,
esClient,
savedObjectsClient,
Expand All @@ -33,7 +31,7 @@ export const getDetectionsMetrics = async ({
}: GetDetectionsMetricsOptions): Promise<DetectionMetrics> => {
const [mlJobMetrics, detectionRuleMetrics] = await Promise.allSettled([
getMlJobMetrics({ mlClient, savedObjectsClient, logger }),
getRuleMetrics({ kibanaIndex, signalsIndex, esClient, savedObjectsClient, logger }),
getRuleMetrics({ signalsIndex, esClient, savedObjectsClient, logger }),
]);

return {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
* 2.0.
*/

import type { SearchResponse } from '@elastic/elasticsearch/lib/api/types';
import type { SavedObjectsFindResponse } from 'kibana/server';
import type { RuleSearchResult } from '../../types';

export const getMockListModulesResponse = () => [
{
Expand Down Expand Up @@ -291,102 +292,100 @@ export const getMockMlDatafeedStatsResponse = () => ({

export const getMockRuleSearchResponse = (
immutableTag: string = '__internal_immutable:true'
): SearchResponse<unknown, unknown> => ({
took: 2,
timed_out: false,
_shards: {
): SavedObjectsFindResponse<RuleSearchResult, never> =>
({
page: 1,
per_page: 1_000,
total: 1,
successful: 1,
skipped: 0,
failed: 0,
},
hits: {
total: {
value: 1093,
relation: 'eq',
},
max_score: 0,
hits: [
saved_objects: [
{
_index: '.kibanaindex',
_id: 'alert:6eecd8c2-8bfb-11eb-afbe-1b7a66309c6d',
_score: 0,
_source: {
alert: {
name: 'Azure Diagnostic Settings Deletion',
tags: [
'Elastic',
'Cloud',
'Azure',
'Continuous Monitoring',
'SecOps',
'Monitoring',
'__internal_rule_id:5370d4cd-2bb3-4d71-abf5-1e1d0ff5a2de',
`${immutableTag}`,
type: 'alert',
id: '6eecd8c2-8bfb-11eb-afbe-1b7a66309c6d',
namespaces: ['default'],
attributes: {
name: 'Azure Diagnostic Settings Deletion',
tags: [
'Elastic',
'Cloud',
'Azure',
'Continuous Monitoring',
'SecOps',
'Monitoring',
'__internal_rule_id:5370d4cd-2bb3-4d71-abf5-1e1d0ff5a2de',
`${immutableTag}`,
],
alertTypeId: 'siem.queryRule',
consumer: 'siem',
params: {
author: ['Elastic'],
description:
'Identifies the deletion of diagnostic settings in Azure, which send platform logs and metrics to different destinations. An adversary may delete diagnostic settings in an attempt to evade defenses.',
ruleId: '5370d4cd-2bb3-4d71-abf5-1e1d0ff5a2de',
index: ['filebeat-*', 'logs-azure*'],
falsePositives: [
'Deletion of diagnostic settings may be done by a system or network administrator. Verify whether the username, hostname, and/or resource name should be making changes in your environment. Diagnostic settings deletion from unfamiliar users or hosts should be investigated. If known behavior is causing false positives, it can be exempted from the rule.',
],
alertTypeId: 'siem.signals',
consumer: 'siem',
params: {
author: ['Elastic'],
description:
'Identifies the deletion of diagnostic settings in Azure, which send platform logs and metrics to different destinations. An adversary may delete diagnostic settings in an attempt to evade defenses.',
ruleId: '5370d4cd-2bb3-4d71-abf5-1e1d0ff5a2de',
index: ['filebeat-*', 'logs-azure*'],
falsePositives: [
'Deletion of diagnostic settings may be done by a system or network administrator. Verify whether the username, hostname, and/or resource name should be making changes in your environment. Diagnostic settings deletion from unfamiliar users or hosts should be investigated. If known behavior is causing false positives, it can be exempted from the rule.',
],
from: 'now-25m',
immutable: true,
query:
'event.dataset:azure.activitylogs and azure.activitylogs.operation_name:"MICROSOFT.INSIGHTS/DIAGNOSTICSETTINGS/DELETE" and event.outcome:(Success or success)',
language: 'kuery',
license: 'Elastic License v2',
outputIndex: '.siem-signals',
maxSignals: 100,
riskScore: 47,
timestampOverride: 'event.ingested',
to: 'now',
type: 'query',
references: [
'https://docs.microsoft.com/en-us/azure/azure-monitor/platform/diagnostic-settings',
],
note: 'The Azure Filebeat module must be enabled to use this rule.',
version: 4,
exceptionsList: [],
},
schedule: {
interval: '5m',
},
enabled: false,
actions: [],
throttle: null,
notifyWhen: 'onActiveAlert',
apiKeyOwner: null,
apiKey: null,
createdBy: 'user',
updatedBy: 'user',
createdAt: '2021-03-23T17:15:59.634Z',
updatedAt: '2021-03-23T17:15:59.634Z',
muteAll: false,
mutedInstanceIds: [],
executionStatus: {
status: 'pending',
lastExecutionDate: '2021-03-23T17:15:59.634Z',
error: null,
},
meta: {
versionApiKeyLastmodified: '8.0.0',
from: 'now-25m',
immutable: true,
query:
'event.dataset:azure.activitylogs and azure.activitylogs.operation_name:"MICROSOFT.INSIGHTS/DIAGNOSTICSETTINGS/DELETE" and event.outcome:(Success or success)',
language: 'kuery',
license: 'Elastic License v2',
outputIndex: '.siem-signals',
maxSignals: 100,
riskScore: 47,
timestampOverride: 'event.ingested',
to: 'now',
type: 'query',
references: [
'https://docs.microsoft.com/en-us/azure/azure-monitor/platform/diagnostic-settings',
],
note: 'The Azure Filebeat module must be enabled to use this rule.',
version: 4,
exceptionsList: [],
},
schedule: {
interval: '5m',
},
enabled: false,
actions: [],
throttle: null,
notifyWhen: 'onActiveAlert',
apiKeyOwner: null,
apiKey: '',
legacyId: null,
createdBy: 'user',
updatedBy: 'user',
createdAt: '2021-03-23T17:15:59.634Z',
updatedAt: '2021-03-23T17:15:59.634Z',
muteAll: true,
mutedInstanceIds: [],
monitoring: {
execution: {
history: [],
calculated_metrics: {
success_ratio: 1,
p99: 7981,
p50: 1653,
p95: 6523.699999999996,
},
},
},
type: 'alert',
references: [],
migrationVersion: {
alert: '7.13.0',
meta: {
versionApiKeyLastmodified: '8.2.0',
},
coreMigrationVersion: '8.0.0',
updated_at: '2021-03-23T17:15:59.634Z',
scheduledTaskId: '6eecd8c2-8bfb-11eb-afbe-1b7a66309c6d',
},
references: [],
migrationVersion: {
alert: '8.0.0',
},
coreMigrationVersion: '8.2.0',
updated_at: '2021-03-23T17:15:59.634Z',
version: 'Wzk4NTQwLDNd',
score: 0,
sort: ['1644865254209', '19548'],
},
],
},
});
// NOTE: We have to cast as "unknown" and then back to "RuleSearchResult" because "RuleSearchResult" isn't an exact type. See notes in the JSDocs fo that type.
} as unknown as SavedObjectsFindResponse<RuleSearchResult, never>);
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,7 @@
* 2.0.
*/

import type {
KibanaRequest,
SavedObjectsClientContract,
Logger,
} from '../../../../../../../src/core/server';
import type { KibanaRequest, SavedObjectsClientContract, Logger } from 'kibana/server';
import type { MlDatafeedStats, MlJob, MlPluginSetup } from '../../../../../ml/server';
import type { MlJobMetric, MlJobUsageMetric } from './types';

Expand Down Expand Up @@ -91,8 +87,8 @@ export const getMlJobMetrics = async ({
};
} catch (e) {
// ignore failure, usage will be zeroed
logger.error(
`Encountered error in telemetry of message: ${e.message}, error: ${e}. Telemetry for "ml_jobs" will be skipped.`
logger.info(
`Encountered exception in telemetry of message: ${e.message}, error: ${e}. Telemetry for "ml_jobs" will be skipped.`
);
return {
ml_job_usage: getInitialMlJobUsage(),
Expand Down
Loading