Skip to content

Commit 2890fb6

Browse files
committed
write record on new index when action request fails to write to .fleet-actions
review comments
1 parent 25b6a9d commit 2890fb6

File tree

2 files changed

+61
-10
lines changed

2 files changed

+61
-10
lines changed

x-pack/plugins/security_solution/common/endpoint/types/actions.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,11 @@ import { ActionStatusRequestSchema, HostIsolationRequestSchema } from '../schema
1111
export type ISOLATION_ACTIONS = 'isolate' | 'unisolate';
1212

1313
interface EcsError {
14-
code: string;
15-
id: string;
14+
code?: string;
15+
id?: string;
1616
message: string;
17-
stack_trace: string;
18-
type: string;
17+
stack_trace?: string;
18+
type?: string;
1919
}
2020

2121
interface EndpointActionFields {

x-pack/plugins/security_solution/server/endpoint/routes/actions/isolation.ts

Lines changed: 57 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import { CasesByAlertId } from '../../../../../cases/common/api/cases/case';
1414
import { HostIsolationRequestSchema } from '../../../../common/endpoint/schema/actions';
1515
import {
1616
ENDPOINT_ACTIONS_DS,
17+
ENDPOINT_ACTION_RESPONSES_DS,
1718
ISOLATE_HOST_ROUTE,
1819
UNISOLATE_HOST_ROUTE,
1920
} from '../../../../common/endpoint/constants';
@@ -22,6 +23,7 @@ import {
2223
EndpointAction,
2324
HostMetadata,
2425
LogsEndpointAction,
26+
LogsEndpointActionResponse,
2527
} from '../../../../common/endpoint/types';
2628
import {
2729
SecuritySolutionPluginRouter,
@@ -60,6 +62,32 @@ export function registerHostIsolationRoutes(
6062
);
6163
}
6264

65+
const createFailedActionResponseEntry = async ({
66+
context,
67+
doc,
68+
logger,
69+
}: {
70+
context: SecuritySolutionRequestHandlerContext;
71+
doc: LogsEndpointActionResponse;
72+
logger: Logger;
73+
}): Promise<void> => {
74+
const esClient = context.core.elasticsearch.client.asCurrentUser;
75+
try {
76+
await esClient.index<LogsEndpointActionResponse>({
77+
index: `${ENDPOINT_ACTION_RESPONSES_DS}-default`,
78+
body: {
79+
...doc,
80+
error: {
81+
code: '424',
82+
message: 'Failed to send action request to agent',
83+
},
84+
},
85+
});
86+
} catch (e) {
87+
logger.error(e);
88+
}
89+
};
90+
6391
const doLogsEndpointActionDsExists = async ({
6492
context,
6593
logger,
@@ -166,12 +194,17 @@ export const isolationRequestHandler = function (
166194
};
167195

168196
// if .logs-endpoint.actions data stream exists
169-
// create action request record in .logs-endpoint.actions DS as the current user
197+
// try to create action request record in .logs-endpoint.actions DS as the current user
198+
// (from >= v7.16, use this check to ensure the current user has privileges to write to the new index)
199+
// and allow only users with superuser privileges to write to fleet indices
200+
const logger = endpointContext.logFactory.get('host-isolation');
170201
const doesLogsEndpointActionsDsExist = await doLogsEndpointActionDsExists({
171202
context,
172-
logger: endpointContext.logFactory.get('host-isolation'),
203+
logger,
173204
dataStreamName: ENDPOINT_ACTIONS_DS,
174205
});
206+
// if the new endpoint indices/data streams exists
207+
// write the action request to the new index as the current user
175208
if (doesLogsEndpointActionsDsExist) {
176209
try {
177210
const esClient = context.core.elasticsearch.client.asCurrentUser;
@@ -197,15 +230,14 @@ export const isolationRequestHandler = function (
197230
}
198231
}
199232

200-
// create action request record as system user in .fleet-actions
201233
try {
202-
// we use this check to ensure the user has permission to write to the new index
203-
// and thus allow this action record to be added to the fleet index specifically by kibana
204234
let esClient = context.core.elasticsearch.client.asCurrentUser;
205235
if (doesLogsEndpointActionsDsExist) {
236+
// create action request record as system user with user in .fleet-actions
206237
esClient = context.core.elasticsearch.client.asInternalUser;
207238
}
208-
239+
// write as the current user if the new indices do not exist
240+
// <v7.16 requires the current user to be super user
209241
fleetActionIndexResult = await esClient.index<EndpointAction>({
210242
index: AGENT_ACTIONS_INDEX,
211243
body: {
@@ -216,6 +248,7 @@ export const isolationRequestHandler = function (
216248
user_id: doc.user.id,
217249
},
218250
});
251+
219252
if (fleetActionIndexResult.statusCode !== 201) {
220253
return res.customError({
221254
statusCode: 500,
@@ -225,6 +258,24 @@ export const isolationRequestHandler = function (
225258
});
226259
}
227260
} catch (e) {
261+
// create entry in .logs-endpoint.action.responses-default data stream
262+
// when writing to .fleet-actions fails
263+
if (doesLogsEndpointActionsDsExist) {
264+
await createFailedActionResponseEntry({
265+
context,
266+
doc: {
267+
'@timestamp': moment().toISOString(),
268+
agent: doc.agent,
269+
EndpointActions: {
270+
action_id: doc.EndpointActions.action_id,
271+
completed_at: moment().toISOString(),
272+
started_at: moment().toISOString(),
273+
data: doc.EndpointActions.data,
274+
},
275+
},
276+
logger,
277+
});
278+
}
228279
return res.customError({
229280
statusCode: 500,
230281
body: { message: e },

0 commit comments

Comments
 (0)