Skip to content

Commit

Permalink
[8.8] [Security Solution] Add support for GET requests to the Detecti…
Browse files Browse the repository at this point in the history
…on Engine health API (#159970) (#160023)

# Backport

This will backport the following commits from `main` to `8.8`:
- [[Security Solution] Add support for GET requests to the Detection
Engine health API
(#159970)](#159970)

<!--- Backport version: 8.9.7 -->

### Questions ?
Please refer to the [Backport tool
documentation](https://github.com/sqren/backport)

<!--BACKPORT [{"author":{"name":"Georgii
Gorbachev","email":"[email protected]"},"sourceCommit":{"committedDate":"2023-06-20T14:03:27Z","message":"[Security
Solution] Add support for GET requests to the Detection Engine health
API (#159970)\n\n**Partially addresses:**
https://github.com/elastic/kibana/issues/125642\r\n\r\n##
Summary\r\n\r\nThe PoC of the Detection Engine health API has been
implemented in\r\nhttps://github.com//pull/157155. Now, we
need to integrate\r\nit into
the\r\n[support-diagnostics](https://github.com/elastic/support-diagnostics)\r\ntool.
It looks like the tool requires the APIs it calls to be callable\r\nwith
the `GET` verb.\r\n\r\nThis PR makes it possible to call 2 out of 3
health endpoints with\r\n`GET`:\r\n\r\n```txt\r\nGET
/internal/detection_engine/health/_cluster\r\n```\r\n\r\n```txt\r\nGET
/internal/detection_engine/health/_space\r\n```\r\n\r\nThe `GET` routes
don't accept any parameters and use the default\r\nparameters
instead:\r\n\r\n- interval: `last_day`\r\n- granularity: `hour`\r\n-
debug: `false`\r\n\r\n\r\n### Checklist\r\n\r\n-
[x]\r\n[Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html)\r\nwas
added for features that require explanation or tutorials\r\n- [ ] [Unit
or
functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere
updated or added to match the most common scenarios\r\n\r\n### For
maintainers\r\n\r\n- [x] This was checked for breaking API changes and
was
[labeled\r\nappropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)","sha":"7047f24c1743a2a98e22e332403c5260d6062374","branchLabelMapping":{"^v8.9.0$":"main","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:skip","Team:Detections
and Resp","Team: SecuritySolution","Feature:Rule
Monitoring","Team:Detection Rule
Management","v8.9.0","v8.8.2"],"number":159970,"url":"https://github.com/elastic/kibana/pull/159970","mergeCommit":{"message":"[Security
Solution] Add support for GET requests to the Detection Engine health
API (#159970)\n\n**Partially addresses:**
https://github.com/elastic/kibana/issues/125642\r\n\r\n##
Summary\r\n\r\nThe PoC of the Detection Engine health API has been
implemented in\r\nhttps://github.com//pull/157155. Now, we
need to integrate\r\nit into
the\r\n[support-diagnostics](https://github.com/elastic/support-diagnostics)\r\ntool.
It looks like the tool requires the APIs it calls to be callable\r\nwith
the `GET` verb.\r\n\r\nThis PR makes it possible to call 2 out of 3
health endpoints with\r\n`GET`:\r\n\r\n```txt\r\nGET
/internal/detection_engine/health/_cluster\r\n```\r\n\r\n```txt\r\nGET
/internal/detection_engine/health/_space\r\n```\r\n\r\nThe `GET` routes
don't accept any parameters and use the default\r\nparameters
instead:\r\n\r\n- interval: `last_day`\r\n- granularity: `hour`\r\n-
debug: `false`\r\n\r\n\r\n### Checklist\r\n\r\n-
[x]\r\n[Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html)\r\nwas
added for features that require explanation or tutorials\r\n- [ ] [Unit
or
functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere
updated or added to match the most common scenarios\r\n\r\n### For
maintainers\r\n\r\n- [x] This was checked for breaking API changes and
was
[labeled\r\nappropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)","sha":"7047f24c1743a2a98e22e332403c5260d6062374"}},"sourceBranch":"main","suggestedTargetBranches":["8.8"],"targetPullRequestStates":[{"branch":"main","label":"v8.9.0","labelRegex":"^v8.9.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/159970","number":159970,"mergeCommit":{"message":"[Security
Solution] Add support for GET requests to the Detection Engine health
API (#159970)\n\n**Partially addresses:**
https://github.com/elastic/kibana/issues/125642\r\n\r\n##
Summary\r\n\r\nThe PoC of the Detection Engine health API has been
implemented in\r\nhttps://github.com//pull/157155. Now, we
need to integrate\r\nit into
the\r\n[support-diagnostics](https://github.com/elastic/support-diagnostics)\r\ntool.
It looks like the tool requires the APIs it calls to be callable\r\nwith
the `GET` verb.\r\n\r\nThis PR makes it possible to call 2 out of 3
health endpoints with\r\n`GET`:\r\n\r\n```txt\r\nGET
/internal/detection_engine/health/_cluster\r\n```\r\n\r\n```txt\r\nGET
/internal/detection_engine/health/_space\r\n```\r\n\r\nThe `GET` routes
don't accept any parameters and use the default\r\nparameters
instead:\r\n\r\n- interval: `last_day`\r\n- granularity: `hour`\r\n-
debug: `false`\r\n\r\n\r\n### Checklist\r\n\r\n-
[x]\r\n[Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html)\r\nwas
added for features that require explanation or tutorials\r\n- [ ] [Unit
or
functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere
updated or added to match the most common scenarios\r\n\r\n### For
maintainers\r\n\r\n- [x] This was checked for breaking API changes and
was
[labeled\r\nappropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)","sha":"7047f24c1743a2a98e22e332403c5260d6062374"}},{"branch":"8.8","label":"v8.8.2","labelRegex":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"}]}]
BACKPORT-->

Co-authored-by: Georgii Gorbachev <[email protected]>
  • Loading branch information
kibanamachine and banderror authored Jun 20, 2023
1 parent 58d19d3 commit bc8ecea
Show file tree
Hide file tree
Showing 3 changed files with 164 additions and 56 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,7 @@ Response:

```txt
POST /internal/detection_engine/health/_space
GET /internal/detection_engine/health/_space
```

Get health overview of the current Kibana space. Scope: all detection rules in the space.
Expand All @@ -374,12 +375,18 @@ Returns:
- health stats history within the same interval in the form of a histogram
(the same stats are calculated over each of the discreet sub-intervals of the whole interval)

Minimal required parameters: empty object.
Minimal required parameters for the `POST` route: empty object.

```json
{}
```

The `GET` route don't accept any parameters and use the default parameters instead:

- interval: `last_day`
- granularity: `hour`
- debug: `false`

Response:

```json
Expand Down Expand Up @@ -721,14 +728,21 @@ Response:

```txt
POST /internal/detection_engine/health/_cluster
GET /internal/detection_engine/health/_cluster
```

Minimal required parameters: empty object.
Minimal required parameters for the `POST` route: empty object.

```json
{}
```

The `GET` route don't accept any parameters and use the default parameters instead:

- interval: `last_day`
- granularity: `hour`
- debug: `false`

Response:

```json
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,21 @@
* 2.0.
*/

import type { KibanaResponseFactory } from '@kbn/core-http-server';
import { transformError } from '@kbn/securitysolution-es-utils';
import { buildRouteValidation } from '../../../../../../utils/build_validation/route_validation';
import { buildSiemResponse } from '../../../../routes/utils';
import type { SecuritySolutionPluginRouter } from '../../../../../../types';

import type { GetClusterHealthResponse } from '../../../../../../../common/detection_engine/rule_monitoring';
import type {
GetClusterHealthRequest,
GetClusterHealthResponse,
} from '../../../../../../../common/detection_engine/rule_monitoring';
import {
GET_CLUSTER_HEALTH_URL,
GetClusterHealthRequestBody,
} from '../../../../../../../common/detection_engine/rule_monitoring';
import type { IDetectionEngineHealthClient } from '../../../logic/detection_engine_health';
import { calculateHealthTimings } from '../health_timings';
import { validateGetClusterHealthRequest } from './get_cluster_health_request';

Expand All @@ -27,6 +32,27 @@ import { validateGetClusterHealthRequest } from './get_cluster_health_request';
* (the same stats are calculated over each of the discreet sub-intervals of the whole interval)
*/
export const getClusterHealthRoute = (router: SecuritySolutionPluginRouter) => {
router.get(
{
path: GET_CLUSTER_HEALTH_URL,
validate: {},
options: {
tags: ['access:securitySolution'],
},
},
async (context, request, response) => {
return handleClusterHealthRequest({
response,
resolveParameters: () => validateGetClusterHealthRequest({}),
resolveDependencies: async () => {
const ctx = await context.resolve(['securitySolution']);
const healthClient = ctx.securitySolution.getDetectionEngineHealthClient();
return { healthClient };
},
});
}
);

router.post(
{
path: GET_CLUSTER_HEALTH_URL,
Expand All @@ -38,36 +64,57 @@ export const getClusterHealthRoute = (router: SecuritySolutionPluginRouter) => {
},
},
async (context, request, response) => {
const siemResponse = buildSiemResponse(response);
return handleClusterHealthRequest({
response,
resolveParameters: () => validateGetClusterHealthRequest(request.body),
resolveDependencies: async () => {
const ctx = await context.resolve(['securitySolution']);
const healthClient = ctx.securitySolution.getDetectionEngineHealthClient();
return { healthClient };
},
});
}
);
};

try {
const params = validateGetClusterHealthRequest(request.body);
interface ClusterHealthRouteDependencies {
healthClient: IDetectionEngineHealthClient;
}

const ctx = await context.resolve(['securitySolution']);
const healthClient = ctx.securitySolution.getDetectionEngineHealthClient();
interface HandleClusterHealthRequestArgs {
response: KibanaResponseFactory;
resolveParameters: () => GetClusterHealthRequest;
resolveDependencies: () => Promise<ClusterHealthRouteDependencies>;
}

const clusterHealthParameters = { interval: params.interval };
const clusterHealth = await healthClient.calculateClusterHealth(clusterHealthParameters);
const handleClusterHealthRequest = async (args: HandleClusterHealthRequestArgs) => {
const { response, resolveParameters, resolveDependencies } = args;
const siemResponse = buildSiemResponse(response);

const responseBody: GetClusterHealthResponse = {
// TODO: https://github.com/elastic/kibana/issues/125642 Implement the endpoint and remove the `message` property
message: 'Not implemented',
timings: calculateHealthTimings(params.requestReceivedAt),
parameters: clusterHealthParameters,
health: {
...clusterHealth,
debug: params.debug ? clusterHealth.debug : undefined,
},
};
try {
const params = resolveParameters();
const { healthClient } = await resolveDependencies();

return response.ok({ body: responseBody });
} catch (err) {
const error = transformError(err);
return siemResponse.error({
body: error.message,
statusCode: error.statusCode,
});
}
}
);
const clusterHealthParameters = { interval: params.interval };
const clusterHealth = await healthClient.calculateClusterHealth(clusterHealthParameters);

const responseBody: GetClusterHealthResponse = {
// TODO: https://github.com/elastic/kibana/issues/125642 Implement the endpoint and remove the `message` property
message: 'Not implemented',
timings: calculateHealthTimings(params.requestReceivedAt),
parameters: clusterHealthParameters,
health: {
...clusterHealth,
debug: params.debug ? clusterHealth.debug : undefined,
},
};

return response.ok({ body: responseBody });
} catch (err) {
const error = transformError(err);
return siemResponse.error({
body: error.message,
statusCode: error.statusCode,
});
}
};
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,21 @@
* 2.0.
*/

import type { KibanaResponseFactory } from '@kbn/core-http-server';
import { transformError } from '@kbn/securitysolution-es-utils';
import { buildRouteValidation } from '../../../../../../utils/build_validation/route_validation';
import { buildSiemResponse } from '../../../../routes/utils';
import type { SecuritySolutionPluginRouter } from '../../../../../../types';

import type { GetSpaceHealthResponse } from '../../../../../../../common/detection_engine/rule_monitoring';
import type {
GetSpaceHealthRequest,
GetSpaceHealthResponse,
} from '../../../../../../../common/detection_engine/rule_monitoring';
import {
GET_SPACE_HEALTH_URL,
GetSpaceHealthRequestBody,
} from '../../../../../../../common/detection_engine/rule_monitoring';
import type { IDetectionEngineHealthClient } from '../../../logic/detection_engine_health';
import { calculateHealthTimings } from '../health_timings';
import { validateGetSpaceHealthRequest } from './get_space_health_request';

Expand All @@ -27,6 +32,27 @@ import { validateGetSpaceHealthRequest } from './get_space_health_request';
* (the same stats are calculated over each of the discreet sub-intervals of the whole interval)
*/
export const getSpaceHealthRoute = (router: SecuritySolutionPluginRouter) => {
router.get(
{
path: GET_SPACE_HEALTH_URL,
validate: {},
options: {
tags: ['access:securitySolution'],
},
},
async (context, request, response) => {
return handleSpaceHealthRequest({
response,
resolveParameters: () => validateGetSpaceHealthRequest({}),
resolveDependencies: async () => {
const ctx = await context.resolve(['securitySolution']);
const healthClient = ctx.securitySolution.getDetectionEngineHealthClient();
return { healthClient };
},
});
}
);

router.post(
{
path: GET_SPACE_HEALTH_URL,
Expand All @@ -38,34 +64,55 @@ export const getSpaceHealthRoute = (router: SecuritySolutionPluginRouter) => {
},
},
async (context, request, response) => {
const siemResponse = buildSiemResponse(response);
return handleSpaceHealthRequest({
response,
resolveParameters: () => validateGetSpaceHealthRequest(request.body),
resolveDependencies: async () => {
const ctx = await context.resolve(['securitySolution']);
const healthClient = ctx.securitySolution.getDetectionEngineHealthClient();
return { healthClient };
},
});
}
);
};

try {
const params = validateGetSpaceHealthRequest(request.body);
interface SpaceHealthRouteDependencies {
healthClient: IDetectionEngineHealthClient;
}

const ctx = await context.resolve(['securitySolution']);
const healthClient = ctx.securitySolution.getDetectionEngineHealthClient();
interface HandleSpaceHealthRequestArgs {
response: KibanaResponseFactory;
resolveParameters: () => GetSpaceHealthRequest;
resolveDependencies: () => Promise<SpaceHealthRouteDependencies>;
}

const spaceHealthParameters = { interval: params.interval };
const spaceHealth = await healthClient.calculateSpaceHealth(spaceHealthParameters);
const handleSpaceHealthRequest = async (args: HandleSpaceHealthRequestArgs) => {
const { response, resolveParameters, resolveDependencies } = args;
const siemResponse = buildSiemResponse(response);

const responseBody: GetSpaceHealthResponse = {
timings: calculateHealthTimings(params.requestReceivedAt),
parameters: spaceHealthParameters,
health: {
...spaceHealth,
debug: params.debug ? spaceHealth.debug : undefined,
},
};
try {
const params = resolveParameters();
const { healthClient } = await resolveDependencies();

return response.ok({ body: responseBody });
} catch (err) {
const error = transformError(err);
return siemResponse.error({
body: error.message,
statusCode: error.statusCode,
});
}
}
);
const spaceHealthParameters = { interval: params.interval };
const spaceHealth = await healthClient.calculateSpaceHealth(spaceHealthParameters);

const responseBody: GetSpaceHealthResponse = {
timings: calculateHealthTimings(params.requestReceivedAt),
parameters: spaceHealthParameters,
health: {
...spaceHealth,
debug: params.debug ? spaceHealth.debug : undefined,
},
};

return response.ok({ body: responseBody });
} catch (err) {
const error = transformError(err);
return siemResponse.error({
body: error.message,
statusCode: error.statusCode,
});
}
};

0 comments on commit bc8ecea

Please sign in to comment.