-
Notifications
You must be signed in to change notification settings - Fork 8.3k
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
[Security Solution][Endpoint] Add ability for users to release
an isolated host in serverless tiers where Response Actions are not available
#163616
Changes from 17 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 is license is Enterprise and user has access to any | ||
// of the response actions with exception of `release`. Sole access to `release` is something | ||
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. nit: |
||
// that is supported for a user in a license downgrade scenario, and in that case we don't want | ||
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. nit: |
||
// to allow access to Response Console. | ||
authz.canAccessResponseConsole = | ||
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. enables Response Console access if the user has Authz to any response action with the exception of 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. |
||
isEnterpriseLicense && | ||
Object.values(omit(RESPONSE_CONSOLE_ACTION_COMMANDS_TO_REQUIRED_AUTHZ, 'release')).some( | ||
(responseActionAuthzKey) => { | ||
return authz[responseActionAuthzKey]; | ||
} | ||
); | ||
Comment on lines
+134
to
+138
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(() => { | ||
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 === false) { | ||
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. nit: |
||
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 : []; | ||
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. | ||
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'); | ||
}); | ||
}); | ||
} | ||
); |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,7 +12,7 @@ import { getEndpointManagementPageList } from '../../../screens/endpoint_managem | |
import { ensureResponseActionAuthzAccess } from '../../../tasks/endpoint_management'; | ||
|
||
describe( | ||
'App Features for Complete PLI', | ||
'App Features for Security Complete PLI', | ||
{ | ||
env: { | ||
ftrConfig: { productTypes: [{ product_line: 'security', product_tier: 'complete' }] }, | ||
|
@@ -50,10 +50,17 @@ describe( | |
}); | ||
} | ||
|
||
for (const actionName of RESPONSE_ACTION_API_COMMANDS_NAMES) { | ||
// No access to response actions (except `unisolate`) | ||
for (const actionName of RESPONSE_ACTION_API_COMMANDS_NAMES.filter( | ||
(apiName) => apiName !== 'unisolate' | ||
)) { | ||
it(`should not allow access to Response Action: ${actionName}`, () => { | ||
ensureResponseActionAuthzAccess('none', actionName, username, password); | ||
}); | ||
} | ||
|
||
it('should have access to `unisoalte` api', () => { | ||
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. typo: unisolate |
||
ensureResponseActionAuthzAccess('all', 'unisolate', username, password); | ||
}); | ||
} | ||
); |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,7 +12,7 @@ import { ensureResponseActionAuthzAccess } from '../../../tasks/endpoint_managem | |
import { getEndpointManagementPageList } from '../../../screens/endpoint_management'; | ||
|
||
describe( | ||
'App Features for Essential PLI', | ||
'App Features for Security Essential PLI', | ||
{ | ||
env: { | ||
ftrConfig: { | ||
|
@@ -52,10 +52,17 @@ describe( | |
}); | ||
} | ||
|
||
for (const actionName of RESPONSE_ACTION_API_COMMANDS_NAMES) { | ||
it(`should NOT allow access to Response Action: ${actionName}`, () => { | ||
// No access to response actions (except `unisolate`) | ||
for (const actionName of RESPONSE_ACTION_API_COMMANDS_NAMES.filter( | ||
(apiName) => apiName !== 'unisolate' | ||
)) { | ||
it(`should not allow access to Response Action: ${actionName}`, () => { | ||
ensureResponseActionAuthzAccess('none', actionName, username, password); | ||
}); | ||
} | ||
|
||
it('should have access to `unisoalte` api', () => { | ||
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. copy/paste typo |
||
ensureResponseActionAuthzAccess('all', 'unisolate', username, password); | ||
}); | ||
} | ||
); |
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.
nit:
..when license is...