-
Notifications
You must be signed in to change notification settings - Fork 8.6k
[Security Solution][Endpoint] Add ability for users to release an isolated host in serverless tiers where Response Actions are not available
#163616
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
835f427
fd00892
cbe5984
9e0d797
30a8c09
9a88e46
5bfb543
983ae60
58d3121
9f61fac
9b45976
4b30fb5
842da17
f012ad1
7389212
dac3c55
5b21022
b599f28
abcfb85
5ad3ce3
8f1c936
9277f99
ecb8f4c
e759009
3989f83
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -7,6 +7,8 @@ | |
|
|
||
| import type { ENDPOINT_PRIVILEGES, FleetAuthz } from '@kbn/fleet-plugin/common'; | ||
|
|
||
| import { omit } from 'lodash'; | ||
| import { RESPONSE_CONSOLE_ACTION_COMMANDS_TO_REQUIRED_AUTHZ } from '../response_actions/constants'; | ||
| import type { LicenseService } from '../../../license'; | ||
| import type { EndpointAuthz } from '../../types/authz'; | ||
| import type { MaybeImmutable } from '../../types'; | ||
|
|
@@ -82,7 +84,7 @@ export const calculateEndpointAuthz = ( | |
|
|
||
| const canWriteExecuteOperations = hasKibanaPrivilege(fleetAuthz, 'writeExecuteOperations'); | ||
|
|
||
| return { | ||
| const authz: EndpointAuthz = { | ||
| canWriteSecuritySolution, | ||
| canReadSecuritySolution, | ||
| canAccessFleet: fleetAuthz?.fleet.all ?? false, | ||
|
|
@@ -95,22 +97,22 @@ export const calculateEndpointAuthz = ( | |
| canWriteActionsLogManagement, | ||
| canReadActionsLogManagement: canReadActionsLogManagement && isEnterpriseLicense, | ||
| canAccessEndpointActionsLogManagement: canReadActionsLogManagement && isPlatinumPlusLicense, | ||
|
|
||
| // --------------------------------------------------------- | ||
| // Response Actions | ||
| // --------------------------------------------------------- | ||
| canIsolateHost: canIsolateHost && isPlatinumPlusLicense, | ||
| canUnIsolateHost, | ||
| canKillProcess: canWriteProcessOperations && isEnterpriseLicense, | ||
| canSuspendProcess: canWriteProcessOperations && isEnterpriseLicense, | ||
| canGetRunningProcesses: canWriteProcessOperations && isEnterpriseLicense, | ||
| canAccessResponseConsole: | ||
| isEnterpriseLicense && | ||
| (canIsolateHost || | ||
| canUnIsolateHost || | ||
| canWriteProcessOperations || | ||
| canWriteFileOperations || | ||
| canWriteExecuteOperations), | ||
| canAccessResponseConsole: false, // set further below | ||
| canWriteExecuteOperations: canWriteExecuteOperations && isEnterpriseLicense, | ||
| canWriteFileOperations: canWriteFileOperations && isEnterpriseLicense, | ||
|
|
||
| // --------------------------------------------------------- | ||
| // artifacts | ||
| // --------------------------------------------------------- | ||
| canWriteTrustedApplications, | ||
| canReadTrustedApplications, | ||
| canWriteHostIsolationExceptions: canWriteHostIsolationExceptions && isPlatinumPlusLicense, | ||
|
|
@@ -122,6 +124,20 @@ export const calculateEndpointAuthz = ( | |
| canWriteEventFilters, | ||
| canReadEventFilters, | ||
| }; | ||
|
|
||
| // Response console is only accessible when license is Enterprise and user has access to any | ||
| // of the response actions except `release`. Sole access to `release` is something | ||
| // that is supported for a user in a license downgrade scenario, and in that case, we don't want | ||
| // to allow access to Response Console. | ||
| authz.canAccessResponseConsole = | ||
| isEnterpriseLicense && | ||
| Object.values(omit(RESPONSE_CONSOLE_ACTION_COMMANDS_TO_REQUIRED_AUTHZ, 'release')).some( | ||
| (responseActionAuthzKey) => { | ||
| return authz[responseActionAuthzKey]; | ||
| } | ||
| ); | ||
|
Comment on lines
+134
to
+138
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔥 |
||
|
|
||
| return authz; | ||
| }; | ||
|
|
||
| export const getEndpointAuthzInitialState = (): EndpointAuthz => { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -27,7 +27,9 @@ export const useHostIsolationAction = ({ | |
| detailsData, | ||
| isHostIsolationPanelOpen, | ||
| onAddIsolationStatusClick, | ||
| }: UseHostIsolationActionProps) => { | ||
| }: UseHostIsolationActionProps): AlertTableContextMenuItem[] => { | ||
| const { canIsolateHost, canUnIsolateHost } = useUserPrivileges().endpointPrivileges; | ||
|
|
||
| const isEndpointAlert = useMemo(() => { | ||
| return isAlertFromEndpointEvent({ data: detailsData || [] }); | ||
| }, [detailsData]); | ||
|
|
@@ -49,14 +51,14 @@ export const useHostIsolationAction = ({ | |
|
|
||
| const { | ||
| loading: loadingHostIsolationStatus, | ||
| isIsolated: isolationStatus, | ||
| isIsolated: isHostIsolated, | ||
| agentStatus, | ||
| capabilities, | ||
| } = useHostIsolationStatus({ | ||
| agentId, | ||
| }); | ||
|
|
||
| const isolationSupported = useMemo(() => { | ||
| const doesHostSupportIsolation = useMemo(() => { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔥 |
||
| return isEndpointAlert | ||
| ? isIsolationSupported({ | ||
| osName: hostOsFamily, | ||
|
|
@@ -66,46 +68,45 @@ export const useHostIsolationAction = ({ | |
| : false; | ||
| }, [agentVersion, capabilities, hostOsFamily, isEndpointAlert]); | ||
|
|
||
| const isIsolationAllowed = useUserPrivileges().endpointPrivileges.canIsolateHost; | ||
|
|
||
| const isolateHostHandler = useCallback(() => { | ||
| closePopover(); | ||
| if (isolationStatus === false) { | ||
| if (!isHostIsolated) { | ||
| onAddIsolationStatusClick('isolateHost'); | ||
| } else { | ||
| onAddIsolationStatusClick('unisolateHost'); | ||
| } | ||
| }, [closePopover, isolationStatus, onAddIsolationStatusClick]); | ||
| }, [closePopover, isHostIsolated, onAddIsolationStatusClick]); | ||
|
|
||
| return useMemo(() => { | ||
| if ( | ||
| !isEndpointAlert || | ||
| !doesHostSupportIsolation || | ||
| loadingHostIsolationStatus || | ||
| isHostIsolationPanelOpen | ||
| ) { | ||
| return []; | ||
| } | ||
|
|
||
| const isolateHostTitle = isolationStatus === false ? ISOLATE_HOST : UNISOLATE_HOST; | ||
| const menuItems = [ | ||
| { | ||
| key: 'isolate-host-action-item', | ||
| 'data-test-subj': 'isolate-host-action-item', | ||
| disabled: agentStatus === HostStatus.UNENROLLED, | ||
| onClick: isolateHostHandler, | ||
| name: isHostIsolated ? UNISOLATE_HOST : ISOLATE_HOST, | ||
| }, | ||
| ]; | ||
|
|
||
| const hostIsolationAction: AlertTableContextMenuItem[] = useMemo( | ||
| () => | ||
| isIsolationAllowed && | ||
| isEndpointAlert && | ||
| isolationSupported && | ||
| isHostIsolationPanelOpen === false && | ||
| loadingHostIsolationStatus === false | ||
| ? [ | ||
| { | ||
| key: 'isolate-host-action-item', | ||
| 'data-test-subj': 'isolate-host-action-item', | ||
| disabled: agentStatus === HostStatus.UNENROLLED, | ||
| onClick: isolateHostHandler, | ||
| name: isolateHostTitle, | ||
| }, | ||
| ] | ||
| : [], | ||
| [ | ||
| agentStatus, | ||
| isEndpointAlert, | ||
| isHostIsolationPanelOpen, | ||
| isIsolationAllowed, | ||
| isolateHostHandler, | ||
| isolateHostTitle, | ||
| isolationSupported, | ||
| loadingHostIsolationStatus, | ||
| ] | ||
| ); | ||
| return hostIsolationAction; | ||
| return canIsolateHost || (isHostIsolated && canUnIsolateHost) ? menuItems : []; | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This fixes a bug were before it was not showing |
||
| }, [ | ||
| isEndpointAlert, | ||
| doesHostSupportIsolation, | ||
| loadingHostIsolationStatus, | ||
| isHostIsolationPanelOpen, | ||
| agentStatus, | ||
| isolateHostHandler, | ||
| canIsolateHost, | ||
| isHostIsolated, | ||
| canUnIsolateHost, | ||
| ]); | ||
| }; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -396,14 +396,18 @@ const hostIsolationSubFeature: SubFeatureConfig = { | |
| groupType: 'mutually_exclusive', | ||
| privileges: [ | ||
| { | ||
| api: [`${APP_ID}-writeHostIsolationRelease`], | ||
| id: 'host_isolation_all', | ||
| includeIn: 'none', | ||
| name: 'All', | ||
| savedObject: { | ||
| all: [], | ||
| read: [], | ||
| }, | ||
| // FYI: The current set of values below (`api`, `ui`) cover only `release` response action. | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm hopping that tidbits of info like this can help along understanding how the framework around loading different privileges into existing Sub-Features |
||
| // There is a second set of values for API and UI that are added later if `endpointResponseActions` | ||
| // appFeature is enabled. Needed to ensure that in a downgrade of license condition, | ||
| // users are still able to un-isolate a host machine. | ||
| api: [`${APP_ID}-writeHostIsolationRelease`], | ||
| ui: ['writeHostIsolationRelease'], | ||
| }, | ||
| ], | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,60 @@ | ||
| /* | ||
| * 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 { login } from '../../tasks/login'; | ||
| import { | ||
| getConsoleActionMenuItem, | ||
| getUnIsolateActionMenuItem, | ||
| openRowActionMenu, | ||
| visitEndpointList, | ||
| } from '../../screens/endpoint_management'; | ||
| import { | ||
| CyIndexEndpointHosts, | ||
| indexEndpointHosts, | ||
| } from '../../tasks/endpoint_management/index_endpoint_hosts'; | ||
|
|
||
| describe( | ||
| 'When on the Endpoint List in Security Essentials PLI', | ||
| { | ||
| env: { | ||
| ftrConfig: { | ||
| productTypes: [{ product_line: 'security', product_tier: 'essentials' }], | ||
| }, | ||
| }, | ||
| }, | ||
| () => { | ||
| describe('and Isolated hosts exist', () => { | ||
| let indexedEndpointData: CyIndexEndpointHosts; | ||
|
|
||
| before(() => { | ||
| indexEndpointHosts({ isolation: true }).then((response) => { | ||
| indexedEndpointData = response; | ||
| }); | ||
| }); | ||
|
|
||
| after(() => { | ||
| if (indexedEndpointData) { | ||
| indexedEndpointData.cleanup(); | ||
| } | ||
| }); | ||
|
|
||
| beforeEach(() => { | ||
| login(); | ||
| visitEndpointList(); | ||
| openRowActionMenu(); | ||
| }); | ||
|
|
||
| it('should display `release` options in host row actions', () => { | ||
| getUnIsolateActionMenuItem().should('exist'); | ||
| }); | ||
|
|
||
| it('should NOT display access to response console', () => { | ||
| getConsoleActionMenuItem().should('not.exist'); | ||
| }); | ||
| }); | ||
| } | ||
| ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
enables Response Console access if the user has Authz to any response action with the exception of
un-isolate. Users that have only permission tounisolateoccurs when the Kibana license is downgraded or in serverless when running a non Endpoint Complete tier. It will allow users to continue toreleasetheir isolated hosts if any.Also - this change here will ensure that as we add more response actions, that access to Response console will continue to pick those up and enable this option.