[Telemetry][Security Solution] Enrich endpoint alerts with license info (#188760)

This commit is contained in:
Sebastián Zaffarano 2024-07-22 12:56:27 +02:00 committed by GitHub
parent 47842f9c43
commit aa6aa26866
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 43 additions and 11 deletions

View file

@ -22,8 +22,8 @@ const asyncUnlink = Util.promisify(Fs.unlink);
*/ */
export async function eventually<T>( export async function eventually<T>(
cb: () => Promise<T>, cb: () => Promise<T>,
duration: number = 60000, duration: number = 120000,
interval: number = 1000 interval: number = 3000
) { ) {
let elapsed = 0; let elapsed = 0;

View file

@ -148,8 +148,7 @@ describe('telemetry tasks', () => {
}); });
}); });
// FLAKY: https://github.com/elastic/kibana/issues/187719 describe('detection-rules', () => {
describe.skip('detection-rules', () => {
it('should execute when scheduled', async () => { it('should execute when scheduled', async () => {
await mockAndScheduleDetectionRulesTask(); await mockAndScheduleDetectionRulesTask();
@ -263,7 +262,7 @@ describe('telemetry tasks', () => {
// wait until the events are sent to the telemetry server // wait until the events are sent to the telemetry server
const body = await eventually(async () => { const body = await eventually(async () => {
const found = mockedAxiosPost.mock.calls.find(([url]) => { const found = mockedAxiosPost.mock.calls.find(([url]) => {
return url.startsWith(ENDPOINT_STAGING) && url.endsWith('alerts-endpoint'); return url.startsWith(ENDPOINT_STAGING) && url.endsWith(TelemetryChannel.ENDPOINT_ALERTS);
}); });
expect(found).not.toBeFalsy(); expect(found).not.toBeFalsy();
@ -274,6 +273,25 @@ describe('telemetry tasks', () => {
expect(body).not.toBeFalsy(); expect(body).not.toBeFalsy();
expect(body.Endpoint).not.toBeFalsy(); expect(body.Endpoint).not.toBeFalsy();
}); });
it('should enrich with license info', async () => {
await mockAndScheduleEndpointDiagnosticsTask();
// wait until the events are sent to the telemetry server
const body = await eventually(async () => {
const found = mockedAxiosPost.mock.calls.find(([url]) => {
return url.startsWith(ENDPOINT_STAGING) && url.endsWith(TelemetryChannel.ENDPOINT_ALERTS);
});
expect(found).not.toBeFalsy();
return JSON.parse((found ? found[1] : '{}') as string);
});
expect(body).not.toBeFalsy();
expect(body.license).not.toBeFalsy();
expect(body.license.status).not.toBeFalsy();
});
}); });
describe('endpoint-meta-telemetry', () => { describe('endpoint-meta-telemetry', () => {
@ -680,8 +698,7 @@ describe('telemetry tasks', () => {
expect(body.file).toStrictEqual(alertsDetectionsRequest.file); expect(body.file).toStrictEqual(alertsDetectionsRequest.file);
}); });
// Flaky: https://github.com/elastic/kibana/issues/188234 it('should manage runtime errors searching endpoint metrics', async () => {
it.skip('should manage runtime errors searching endpoint metrics', async () => {
const errorMessage = 'Something went wront'; const errorMessage = 'Something went wront';
async function* mockedGenerator( async function* mockedGenerator(

View file

@ -21,7 +21,7 @@ import { TelemetryChannel, TelemetryCounter } from './types';
import * as collections from './collections_helpers'; import * as collections from './collections_helpers';
import { CachedSubject, retryOnError$ } from './rxjs_helpers'; import { CachedSubject, retryOnError$ } from './rxjs_helpers';
import { SenderUtils } from './sender_helpers'; import { SenderUtils } from './sender_helpers';
import { newTelemetryLogger } from './helpers'; import { copyLicenseFields, newTelemetryLogger } from './helpers';
import { type TelemetryLogger } from './telemetry_logger'; import { type TelemetryLogger } from './telemetry_logger';
export const DEFAULT_QUEUE_CONFIG: QueueConfig = { export const DEFAULT_QUEUE_CONFIG: QueueConfig = {
@ -291,6 +291,14 @@ export class AsyncTelemetryEventsSender implements IAsyncTelemetryEventsSender {
}; };
} }
if (event.channel === TelemetryChannel.ENDPOINT_ALERTS) {
const licenseInfo = this.telemetryReceiver?.getLicenseInfo();
additional = {
...additional,
...(licenseInfo ? { license: copyLicenseFields(licenseInfo) } : {}),
};
}
event.payload = { event.payload = {
...event.payload, ...event.payload,
...additional, ...additional,

View file

@ -102,6 +102,8 @@ export interface ITelemetryReceiver {
fetchClusterInfo(): Promise<ESClusterInfo>; fetchClusterInfo(): Promise<ESClusterInfo>;
getLicenseInfo(): Nullable<ESLicense>;
fetchLicenseInfo(): Promise<Nullable<ESLicense>>; fetchLicenseInfo(): Promise<Nullable<ESLicense>>;
closePointInTime(pitId: string): Promise<void>; closePointInTime(pitId: string): Promise<void>;
@ -248,6 +250,7 @@ export class TelemetryReceiver implements ITelemetryReceiver {
private getIndexForType?: (type: string) => string; private getIndexForType?: (type: string) => string;
private alertsIndex?: string; private alertsIndex?: string;
private clusterInfo?: ESClusterInfo; private clusterInfo?: ESClusterInfo;
private licenseInfo?: Nullable<ESLicense>;
private processTreeFetcher?: Fetcher; private processTreeFetcher?: Fetcher;
private packageService?: PackageService; private packageService?: PackageService;
private experimentalFeatures: ExperimentalFeatures | undefined; private experimentalFeatures: ExperimentalFeatures | undefined;
@ -280,6 +283,7 @@ export class TelemetryReceiver implements ITelemetryReceiver {
this.soClient = this.soClient =
core?.savedObjects.createInternalRepository() as unknown as SavedObjectsClientContract; core?.savedObjects.createInternalRepository() as unknown as SavedObjectsClientContract;
this.clusterInfo = await this.fetchClusterInfo(); this.clusterInfo = await this.fetchClusterInfo();
this.licenseInfo = await this.fetchLicenseInfo();
this.experimentalFeatures = endpointContextService?.experimentalFeatures; this.experimentalFeatures = endpointContextService?.experimentalFeatures;
const elasticsearch = core?.elasticsearch.client as unknown as IScopedClusterClient; const elasticsearch = core?.elasticsearch.client as unknown as IScopedClusterClient;
this.processTreeFetcher = new Fetcher(elasticsearch); this.processTreeFetcher = new Fetcher(elasticsearch);
@ -291,6 +295,10 @@ export class TelemetryReceiver implements ITelemetryReceiver {
return this.clusterInfo; return this.clusterInfo;
} }
public getLicenseInfo(): Nullable<ESLicense> {
return this.licenseInfo;
}
public getAlertsIndex(): string | undefined { public getAlertsIndex(): string | undefined {
return this.alertsIndex; return this.alertsIndex;
} }

View file

@ -8,11 +8,10 @@
import type { Logger } from '@kbn/core/server'; import type { Logger } from '@kbn/core/server';
import { newTelemetryLogger, getPreviousDiagTaskTimestamp } from '../helpers'; import { newTelemetryLogger, getPreviousDiagTaskTimestamp } from '../helpers';
import type { ITelemetryEventsSender } from '../sender'; import type { ITelemetryEventsSender } from '../sender';
import type { TelemetryEvent } from '../types'; import { TelemetryChannel, type TelemetryEvent } from '../types';
import type { ITelemetryReceiver } from '../receiver'; import type { ITelemetryReceiver } from '../receiver';
import type { TaskExecutionPeriod } from '../task'; import type { TaskExecutionPeriod } from '../task';
import type { ITaskMetricsService } from '../task_metrics.types'; import type { ITaskMetricsService } from '../task_metrics.types';
import { TELEMETRY_CHANNEL_ENDPOINT_ALERTS } from '../constants';
import { copyAllowlistedFields, filterList } from '../filterlists'; import { copyAllowlistedFields, filterList } from '../filterlists';
export function createTelemetryDiagnosticsTaskConfig() { export function createTelemetryDiagnosticsTaskConfig() {
@ -65,7 +64,7 @@ export function createTelemetryDiagnosticsTaskConfig() {
log.l('Sending diagnostic alerts', { log.l('Sending diagnostic alerts', {
alerts_count: alerts.length, alerts_count: alerts.length,
}); });
await sender.sendOnDemand(TELEMETRY_CHANNEL_ENDPOINT_ALERTS, processedAlerts); sender.sendAsync(TelemetryChannel.ENDPOINT_ALERTS, processedAlerts);
} }
await taskMetricsService.end(trace); await taskMetricsService.end(trace);