mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
[Security Solution][Endpoint][Response Actions] Allow access to responder for execute
RBAC (#152004)
## Summary Adds execute RBAC to the list of privileges that allow access to response action console (responder) ### Checklist - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios
This commit is contained in:
parent
b6cff1ad72
commit
31bd2ef37a
4 changed files with 114 additions and 21 deletions
|
@ -10,6 +10,11 @@ import type { FleetAuthz } from '@kbn/fleet-plugin/common';
|
|||
import { createFleetAuthzMock } from '@kbn/fleet-plugin/common/mocks';
|
||||
import { createLicenseServiceMock } from '../../../license/mocks';
|
||||
import type { EndpointAuthzKeyList } from '../../types/authz';
|
||||
import {
|
||||
commandToRBACMap,
|
||||
CONSOLE_RESPONSE_ACTION_COMMANDS,
|
||||
type ResponseConsoleRbacControls,
|
||||
} from '../response_actions/constants';
|
||||
|
||||
describe('Endpoint Authz service', () => {
|
||||
let licenseService: ReturnType<typeof createLicenseServiceMock>;
|
||||
|
@ -121,6 +126,16 @@ describe('Endpoint Authz service', () => {
|
|||
});
|
||||
|
||||
describe('and endpoint rbac is enabled', () => {
|
||||
const responseConsolePrivileges = CONSOLE_RESPONSE_ACTION_COMMANDS.slice().reduce<
|
||||
ResponseConsoleRbacControls[]
|
||||
>((acc, e) => {
|
||||
const item = commandToRBACMap[e];
|
||||
if (!acc.includes(item)) {
|
||||
acc.push(item);
|
||||
}
|
||||
return acc;
|
||||
}, []);
|
||||
|
||||
beforeEach(() => {
|
||||
userRoles = [];
|
||||
});
|
||||
|
@ -182,6 +197,8 @@ describe('Endpoint Authz service', () => {
|
|||
['canReadBlocklist', ['writeBlocklist', 'readBlocklist']],
|
||||
['canWriteEventFilters', ['writeEventFilters']],
|
||||
['canReadEventFilters', ['writeEventFilters', 'readEventFilters']],
|
||||
// all dependent privileges are false and so it should be false
|
||||
['canAccessResponseConsole', responseConsolePrivileges],
|
||||
])('%s should be false if `packagePrivilege.%s` is `false`', (auth, privileges) => {
|
||||
// read permission checks for write || read so we need to set both to false
|
||||
privileges.forEach((privilege) => {
|
||||
|
@ -190,6 +207,23 @@ describe('Endpoint Authz service', () => {
|
|||
const authz = calculateEndpointAuthz(licenseService, fleetAuthz, userRoles, true);
|
||||
expect(authz[auth]).toBe(false);
|
||||
});
|
||||
|
||||
it.each(responseConsolePrivileges)(
|
||||
'canAccessResponseConsole should be true if %s for CONSOLE privileges is true',
|
||||
(responseConsolePrivilege) => {
|
||||
// set all to false
|
||||
responseConsolePrivileges.forEach((p) => {
|
||||
fleetAuthz.packagePrivileges!.endpoint.actions[p].executePackageAction = false;
|
||||
});
|
||||
// set one of them to true
|
||||
fleetAuthz.packagePrivileges!.endpoint.actions[
|
||||
responseConsolePrivilege
|
||||
].executePackageAction = true;
|
||||
|
||||
const authz = calculateEndpointAuthz(licenseService, fleetAuthz, userRoles, true);
|
||||
expect(authz.canAccessResponseConsole).toBe(true);
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -241,7 +241,10 @@ export const calculateEndpointAuthz = (
|
|||
canGetRunningProcesses: canWriteProcessOperations && isEnterpriseLicense,
|
||||
canAccessResponseConsole:
|
||||
isEnterpriseLicense &&
|
||||
(canIsolateHost || canWriteProcessOperations || canWriteFileOperations),
|
||||
(canIsolateHost ||
|
||||
canWriteProcessOperations ||
|
||||
canWriteFileOperations ||
|
||||
canWriteExecuteOperations),
|
||||
canWriteExecuteOperations: canWriteExecuteOperations && isEnterpriseLicense,
|
||||
canWriteFileOperations: canWriteFileOperations && isEnterpriseLicense,
|
||||
// artifacts
|
||||
|
|
|
@ -52,6 +52,26 @@ export const CONSOLE_RESPONSE_ACTION_COMMANDS = [
|
|||
|
||||
export type ConsoleResponseActionCommands = typeof CONSOLE_RESPONSE_ACTION_COMMANDS[number];
|
||||
|
||||
export type ResponseConsoleRbacControls =
|
||||
| 'writeHostIsolation'
|
||||
| 'writeProcessOperations'
|
||||
| 'writeFileOperations'
|
||||
| 'writeExecuteOperations';
|
||||
|
||||
/**
|
||||
* maps the console command to the RBAC control that is required to access it via console
|
||||
*/
|
||||
export const commandToRBACMap: Record<ConsoleResponseActionCommands, ResponseConsoleRbacControls> =
|
||||
Object.freeze({
|
||||
isolate: 'writeHostIsolation',
|
||||
release: 'writeHostIsolation',
|
||||
'kill-process': 'writeProcessOperations',
|
||||
'suspend-process': 'writeProcessOperations',
|
||||
processes: 'writeProcessOperations',
|
||||
'get-file': 'writeFileOperations',
|
||||
execute: 'writeExecuteOperations',
|
||||
});
|
||||
|
||||
// 4 hrs in milliseconds
|
||||
// 4 * 60 * 60 * 1000
|
||||
export const DEFAULT_EXECUTE_ACTION_TIMEOUT = 14400000;
|
||||
|
|
|
@ -64,14 +64,59 @@ const executeTimeoutValidator = (argData: ParsedArgData): true | string => {
|
|||
}
|
||||
};
|
||||
|
||||
const commandToCapabilitiesMap = new Map<ConsoleResponseActionCommands, EndpointCapabilities>([
|
||||
['isolate', 'isolation'],
|
||||
['release', 'isolation'],
|
||||
['kill-process', 'kill_process'],
|
||||
['suspend-process', 'suspend_process'],
|
||||
['processes', 'running_processes'],
|
||||
['get-file', 'get_file'],
|
||||
['execute', 'execute'],
|
||||
const commandToCapabilitiesPrivilegesMap = new Map<
|
||||
ConsoleResponseActionCommands,
|
||||
{ capability: EndpointCapabilities; privilege: (privileges: EndpointPrivileges) => boolean }
|
||||
>([
|
||||
[
|
||||
'isolate',
|
||||
{
|
||||
capability: 'isolation',
|
||||
privilege: (privileges: EndpointPrivileges) => privileges.canIsolateHost,
|
||||
},
|
||||
],
|
||||
[
|
||||
'release',
|
||||
{
|
||||
capability: 'isolation',
|
||||
privilege: (privileges: EndpointPrivileges) => privileges.canUnIsolateHost,
|
||||
},
|
||||
],
|
||||
[
|
||||
'kill-process',
|
||||
{
|
||||
capability: 'kill_process',
|
||||
privilege: (privileges: EndpointPrivileges) => privileges.canKillProcess,
|
||||
},
|
||||
],
|
||||
[
|
||||
'suspend-process',
|
||||
{
|
||||
capability: 'suspend_process',
|
||||
privilege: (privileges: EndpointPrivileges) => privileges.canSuspendProcess,
|
||||
},
|
||||
],
|
||||
[
|
||||
'processes',
|
||||
{
|
||||
capability: 'running_processes',
|
||||
privilege: (privileges: EndpointPrivileges) => privileges.canGetRunningProcesses,
|
||||
},
|
||||
],
|
||||
[
|
||||
'get-file',
|
||||
{
|
||||
capability: 'get_file',
|
||||
privilege: (privileges: EndpointPrivileges) => privileges.canWriteFileOperations,
|
||||
},
|
||||
],
|
||||
[
|
||||
'execute',
|
||||
{
|
||||
capability: 'execute',
|
||||
privilege: (privileges: EndpointPrivileges) => privileges.canWriteExecuteOperations,
|
||||
},
|
||||
],
|
||||
]);
|
||||
|
||||
const getRbacControl = ({
|
||||
|
@ -81,23 +126,14 @@ const getRbacControl = ({
|
|||
commandName: ConsoleResponseActionCommands;
|
||||
privileges: EndpointPrivileges;
|
||||
}): boolean => {
|
||||
const commandToPrivilegeMap = new Map<ConsoleResponseActionCommands, boolean>([
|
||||
['isolate', privileges.canIsolateHost],
|
||||
['release', privileges.canUnIsolateHost],
|
||||
['kill-process', privileges.canKillProcess],
|
||||
['suspend-process', privileges.canSuspendProcess],
|
||||
['processes', privileges.canGetRunningProcesses],
|
||||
['get-file', privileges.canWriteFileOperations],
|
||||
['execute', privileges.canWriteExecuteOperations],
|
||||
]);
|
||||
return commandToPrivilegeMap.get(commandName as ConsoleResponseActionCommands) ?? false;
|
||||
return Boolean(commandToCapabilitiesPrivilegesMap.get(commandName)?.privilege(privileges));
|
||||
};
|
||||
|
||||
const capabilitiesAndPrivilegesValidator = (command: Command): true | string => {
|
||||
const privileges = command.commandDefinition.meta.privileges;
|
||||
const endpointCapabilities: EndpointCapabilities[] = command.commandDefinition.meta.capabilities;
|
||||
const commandName = command.commandDefinition.name as ConsoleResponseActionCommands;
|
||||
const responderCapability = commandToCapabilitiesMap.get(commandName);
|
||||
const responderCapability = commandToCapabilitiesPrivilegesMap.get(commandName)?.capability;
|
||||
let errorMessage = '';
|
||||
if (!responderCapability) {
|
||||
errorMessage = errorMessage.concat(UPGRADE_ENDPOINT_FOR_RESPONDER);
|
||||
|
@ -155,7 +191,7 @@ export const getEndpointConsoleCommands = ({
|
|||
const isExecuteEnabled = ExperimentalFeaturesService.get().responseActionExecuteEnabled;
|
||||
|
||||
const doesEndpointSupportCommand = (commandName: ConsoleResponseActionCommands) => {
|
||||
const responderCapability = commandToCapabilitiesMap.get(commandName);
|
||||
const responderCapability = commandToCapabilitiesPrivilegesMap.get(commandName)?.capability;
|
||||
if (responderCapability) {
|
||||
return endpointCapabilities.includes(responderCapability);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue