diff --git a/x-pack/plugins/security_solution/common/endpoint/data_generators/host_isolation_exception_generator.ts b/x-pack/plugins/security_solution/common/endpoint/data_generators/host_isolation_exception_generator.ts new file mode 100644 index 0000000000000..1790924182dfc --- /dev/null +++ b/x-pack/plugins/security_solution/common/endpoint/data_generators/host_isolation_exception_generator.ts @@ -0,0 +1,39 @@ +/* + * 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 { CreateExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types'; +import { ENDPOINT_HOST_ISOLATION_EXCEPTIONS_LIST_ID } from '@kbn/securitysolution-list-constants'; +import { BaseDataGenerator } from './base_data_generator'; +import { getCreateExceptionListItemSchemaMock } from '../../../../lists/common/schemas/request/create_exception_list_item_schema.mock'; + +export class HostIsolationExceptionGenerator extends BaseDataGenerator { + generate(): CreateExceptionListItemSchema { + const overrides: Partial = { + name: `generator exception ${this.randomString(5)}`, + list_id: ENDPOINT_HOST_ISOLATION_EXCEPTIONS_LIST_ID, + item_id: `generator_endpoint_host_isolation_exception_${this.randomUUID()}`, + os_types: ['windows', 'linux', 'macos'], + tags: ['policy:all'], + namespace_type: 'agnostic', + meta: undefined, + description: `Description ${this.randomString(5)}`, + entries: [ + { + field: 'destination.ip', + operator: 'included', + type: 'match', + value: this.randomIP(), + }, + ], + }; + + return Object.assign>( + getCreateExceptionListItemSchemaMock(), + overrides + ); + } +} diff --git a/x-pack/plugins/security_solution/scripts/endpoint/host_isolation_exceptions/index.ts b/x-pack/plugins/security_solution/scripts/endpoint/host_isolation_exceptions/index.ts new file mode 100644 index 0000000000000..b9b70c4c1da19 --- /dev/null +++ b/x-pack/plugins/security_solution/scripts/endpoint/host_isolation_exceptions/index.ts @@ -0,0 +1,111 @@ +/* + * 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 { run, RunFn, createFailError } from '@kbn/dev-utils'; +import { KbnClient } from '@kbn/test'; +import { AxiosError } from 'axios'; +import bluebird from 'bluebird'; +import type { CreateExceptionListSchema } from '@kbn/securitysolution-io-ts-list-types'; +import { + ENDPOINT_HOST_ISOLATION_EXCEPTIONS_LIST_DESCRIPTION, + ENDPOINT_HOST_ISOLATION_EXCEPTIONS_LIST_ID, + ENDPOINT_HOST_ISOLATION_EXCEPTIONS_LIST_NAME, + EXCEPTION_LIST_ITEM_URL, + EXCEPTION_LIST_URL, +} from '@kbn/securitysolution-list-constants'; +import { HostIsolationExceptionGenerator } from '../../../common/endpoint/data_generators/host_isolation_exception_generator'; + +export const cli = () => { + run( + async (options) => { + try { + await createHostIsolationException(options); + options.log.success(`${options.flags.count} endpoint host isolation exceptions`); + } catch (e) { + options.log.error(e); + throw createFailError(e.message); + } + }, + { + description: 'Load Host Isolation Exceptions', + flags: { + string: ['kibana'], + default: { + count: 10, + kibana: 'http://elastic:changeme@localhost:5601', + }, + help: ` + --count Number of host isolation exceptions to create. Default: 10 + --kibana The URL to kibana including credentials. Default: http://elastic:changeme@localhost:5601 + `, + }, + } + ); +}; + +class EventFilterDataLoaderError extends Error { + constructor(message: string, public readonly meta: unknown) { + super(message); + } +} + +const handleThrowAxiosHttpError = (err: AxiosError): never => { + let message = err.message; + + if (err.response) { + message = `[${err.response.status}] ${err.response.data.message ?? err.message} [ ${String( + err.response.config.method + ).toUpperCase()} ${err.response.config.url} ]`; + } + throw new EventFilterDataLoaderError(message, err.toJSON()); +}; + +const createHostIsolationException: RunFn = async ({ flags, log }) => { + const eventGenerator = new HostIsolationExceptionGenerator(); + const kbn = new KbnClient({ log, url: flags.kibana as string }); + + await ensureCreateEndpointHostIsolationExceptionList(kbn); + + await bluebird.map( + Array.from({ length: flags.count as unknown as number }), + () => + kbn + .request({ + method: 'POST', + path: EXCEPTION_LIST_ITEM_URL, + body: eventGenerator.generate(), + }) + .catch((e) => handleThrowAxiosHttpError(e)), + { concurrency: 10 } + ); +}; + +const ensureCreateEndpointHostIsolationExceptionList = async (kbn: KbnClient) => { + const newListDefinition: CreateExceptionListSchema = { + description: ENDPOINT_HOST_ISOLATION_EXCEPTIONS_LIST_DESCRIPTION, + list_id: ENDPOINT_HOST_ISOLATION_EXCEPTIONS_LIST_ID, + meta: undefined, + name: ENDPOINT_HOST_ISOLATION_EXCEPTIONS_LIST_NAME, + os_types: [], + tags: [], + type: 'endpoint', + namespace_type: 'agnostic', + }; + + await kbn + .request({ + method: 'POST', + path: EXCEPTION_LIST_URL, + body: newListDefinition, + }) + .catch((e) => { + // Ignore if list was already created + if (e.response.status !== 409) { + handleThrowAxiosHttpError(e); + } + }); +}; diff --git a/x-pack/plugins/security_solution/scripts/endpoint/load_host_isolation_exceptions.js b/x-pack/plugins/security_solution/scripts/endpoint/load_host_isolation_exceptions.js new file mode 100644 index 0000000000000..13fedecb690ca --- /dev/null +++ b/x-pack/plugins/security_solution/scripts/endpoint/load_host_isolation_exceptions.js @@ -0,0 +1,11 @@ +#!/usr/bin/env node + +/* + * 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. + */ + +require('../../../../../src/setup_node_env'); +require('./host_isolation_exceptions').cli();