mirror of
https://github.com/elastic/kibana.git
synced 2025-06-27 18:51:07 -04:00
[Defend 4 containers] ignoreVolumeMounts + ignoreVolumeFiles selector conditions added. (#151956)
## Summary This adds two new selector conditions to our policy schema. ignoreVolumeMounts and ignoreVolumeFiles. They are a easy route to avoid users having to list all their k8s mounts in targetFilePath selector (as an exclude selector). This PR also refactors the json schema for a policy into it's own json file, so that it may be sync with the json file in the cloud-defend repo. Bonus commit: selectors now support "createFile, modifyFile and deleteFile" operations. (FIM capabilities) Context: https://github.com/elastic/security-team/issues/5718 https://github.com/elastic/integrations/tree/main/packages/cloud_defend#selectors  ### Checklist Delete any items that are not applicable to this PR. - [x] Any text added follows [EUI's writing guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses sentence case text and includes [i18n support](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md) - [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 - [x] Any UI touched in this PR is usable by keyboard only (learn more about [keyboard accessibility](https://webaim.org/techniques/keyboard/)) - [x] Any UI touched in this PR does not create any new axe failures (run axe in browser: [FF](https://addons.mozilla.org/en-US/firefox/addon/axe-devtools/), [Chrome](https://chrome.google.com/webstore/detail/axe-web-accessibility-tes/lhdoppojpmngadmnindnejefpokejbdd?hl=en-US)) - [x] This renders correctly on smaller devices using a responsive layout. (You can test this [in your browser](https://www.browserstack.com/guide/responsive-testing-on-local-server)) - [x] This was checked for [cross-browser compatibility](https://www.elastic.co/support/matrix#matrix_browsers)
This commit is contained in:
parent
834c8ca551
commit
308ebf64cb
8 changed files with 422 additions and 170 deletions
|
@ -21,7 +21,7 @@ export const selectors = i18n.translate('xpack.cloudDefend.controlSelectors', {
|
|||
});
|
||||
|
||||
export const selectorsHelp = i18n.translate('xpack.cloudDefend.controlSelectorsHelp', {
|
||||
defaultMessage: 'Create selectors to match on activities that should be blocked or alerted.',
|
||||
defaultMessage: 'Create selectors to match on operations that should be blocked or alerted.',
|
||||
});
|
||||
|
||||
export const responses = i18n.translate('xpack.cloudDefend.controlResponses', {
|
||||
|
@ -99,6 +99,22 @@ export const errorValueLengthExceeded = i18n.translate(
|
|||
}
|
||||
);
|
||||
|
||||
export const getConditionHelpLabel = (prop: string) => {
|
||||
switch (prop) {
|
||||
case ControlSelectorCondition.ignoreVolumeMounts:
|
||||
return i18n.translate('xpack.cloudDefend.ignoreVolumeMountsHelp', {
|
||||
defaultMessage: 'Ignore operations on all volume mounts.',
|
||||
});
|
||||
case ControlSelectorCondition.ignoreVolumeFiles:
|
||||
return i18n.translate('xpack.cloudDefend.ignoreVolumeFilesHelp', {
|
||||
defaultMessage:
|
||||
'Ignore operations on file mounts only. e.g mounted files, configMaps, secrets etc...',
|
||||
});
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
};
|
||||
|
||||
export const getConditionLabel = (prop: string) => {
|
||||
switch (prop) {
|
||||
case ControlSelectorCondition.operation:
|
||||
|
@ -117,6 +133,14 @@ export const getConditionLabel = (prop: string) => {
|
|||
return i18n.translate('xpack.cloudDefend.targetFilePath', {
|
||||
defaultMessage: 'Target file path',
|
||||
});
|
||||
case ControlSelectorCondition.ignoreVolumeFiles:
|
||||
return i18n.translate('xpack.cloudDefend.ignoreVolumeFiles', {
|
||||
defaultMessage: 'Ignore volume files',
|
||||
});
|
||||
case ControlSelectorCondition.ignoreVolumeMounts:
|
||||
return i18n.translate('xpack.cloudDefend.ignoreVolumeMounts', {
|
||||
defaultMessage: 'Ignore volume mounts',
|
||||
});
|
||||
case ControlSelectorCondition.orchestratorClusterId:
|
||||
return i18n.translate('xpack.cloudDefend.orchestratorClusterId', {
|
||||
defaultMessage: 'Orchestrator cluster ID',
|
||||
|
@ -141,5 +165,7 @@ export const getConditionLabel = (prop: string) => {
|
|||
return i18n.translate('xpack.cloudDefend.orchestratorResourceType', {
|
||||
defaultMessage: 'Orchestrator resource type',
|
||||
});
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
};
|
||||
|
|
|
@ -118,6 +118,21 @@ describe('<ControlGeneralViewSelector />', () => {
|
|||
expect(updatedOptions[0]).not.toHaveTextContent('containerImageName');
|
||||
});
|
||||
|
||||
it('allows the user add boolean type conditions', async () => {
|
||||
const { getByTestId, rerender } = render(<WrappedComponent />);
|
||||
const addConditionBtn = getByTestId('cloud-defend-btnaddselectorcondition');
|
||||
|
||||
userEvent.click(addConditionBtn);
|
||||
|
||||
const addIgnoreVolumeMounts = getByTestId('cloud-defend-addmenu-ignoreVolumeMounts');
|
||||
|
||||
await waitFor(() => userEvent.click(addIgnoreVolumeMounts));
|
||||
|
||||
const updatedSelector: ControlSelector = { ...onChange.mock.calls[0][0] };
|
||||
rerender(<WrappedComponent selector={updatedSelector} />);
|
||||
expect(updatedSelector.ignoreVolumeMounts).toBeTruthy();
|
||||
});
|
||||
|
||||
it('shows an error if no conditions are added', async () => {
|
||||
const { getByText, getByTestId, rerender } = render(<WrappedComponent />);
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@ import {
|
|||
EuiSpacer,
|
||||
EuiFlexGroup,
|
||||
EuiFlexItem,
|
||||
EuiText,
|
||||
} from '@elastic/eui';
|
||||
import { useStyles } from './styles';
|
||||
import {
|
||||
|
@ -26,6 +27,7 @@ import {
|
|||
ControlFormErrorMap,
|
||||
ControlSelectorCondition,
|
||||
ControlSelectorConditionUIOptionsMap,
|
||||
ControlSelectorBooleanConditions,
|
||||
ControlSelector,
|
||||
} from '../../types';
|
||||
import * as i18n from '../control_general_view/translations';
|
||||
|
@ -36,6 +38,104 @@ import {
|
|||
MAX_FILE_PATH_VALUE_LENGTH_BYTES,
|
||||
} from '../../common/constants';
|
||||
|
||||
interface ConditionProps {
|
||||
label: string;
|
||||
prop: string;
|
||||
onRemoveCondition(prop: string): void;
|
||||
}
|
||||
|
||||
interface StringArrayConditionProps extends ConditionProps {
|
||||
selector: ControlSelector;
|
||||
errorMap: ControlFormErrorMap;
|
||||
onAddValueToCondition(prop: string, value: string): void;
|
||||
onChangeStringArrayCondition(prop: string, value: string[]): void;
|
||||
}
|
||||
|
||||
const BooleanCondition = ({ label, prop, onRemoveCondition }: ConditionProps) => {
|
||||
return (
|
||||
<EuiFormRow label={label} fullWidth={true} key={prop}>
|
||||
<EuiFlexGroup alignItems="center" gutterSize="m">
|
||||
<EuiFlexItem>
|
||||
<EuiText size="s">
|
||||
<p>
|
||||
<small>{i18n.getConditionHelpLabel(prop)}</small>
|
||||
</p>
|
||||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButtonIcon
|
||||
iconType="cross"
|
||||
onClick={() => onRemoveCondition(prop)}
|
||||
aria-label="Remove condition"
|
||||
data-test-subj={'cloud-defend-btnremovecondition-' + prop}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiFormRow>
|
||||
);
|
||||
};
|
||||
|
||||
const StringArrayCondition = ({
|
||||
label,
|
||||
prop,
|
||||
selector,
|
||||
errorMap,
|
||||
onRemoveCondition,
|
||||
onAddValueToCondition,
|
||||
onChangeStringArrayCondition,
|
||||
}: StringArrayConditionProps) => {
|
||||
const values = selector[prop as keyof ControlSelector] as string[];
|
||||
const selectedOptions =
|
||||
values?.map((option) => {
|
||||
return { label: option, value: option };
|
||||
}) || [];
|
||||
|
||||
const restrictedValues = ControlSelectorConditionUIOptionsMap[prop]?.values;
|
||||
|
||||
return (
|
||||
<EuiFormRow
|
||||
label={label}
|
||||
fullWidth={true}
|
||||
key={prop}
|
||||
isInvalid={!!errorMap.hasOwnProperty(prop)}
|
||||
>
|
||||
<EuiFlexGroup alignItems="center" gutterSize="m">
|
||||
<EuiFlexItem>
|
||||
<EuiComboBox
|
||||
aria-label={label}
|
||||
fullWidth={true}
|
||||
onCreateOption={
|
||||
!restrictedValues
|
||||
? (searchValue) => onAddValueToCondition(prop, searchValue)
|
||||
: undefined
|
||||
}
|
||||
selectedOptions={selectedOptions}
|
||||
options={
|
||||
restrictedValues
|
||||
? restrictedValues.map((value: string) => ({ label: value, value }))
|
||||
: selectedOptions
|
||||
}
|
||||
onChange={(options) =>
|
||||
onChangeStringArrayCondition(prop, options.map((option) => option.value) as string[])
|
||||
}
|
||||
isClearable
|
||||
data-test-subj={'cloud-defend-selectorcondition-' + prop}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButtonIcon
|
||||
iconType="cross"
|
||||
onClick={() => onRemoveCondition(prop)}
|
||||
aria-label="Remove condition"
|
||||
data-test-subj={'cloud-defend-btnremovecondition-' + prop}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiFormRow>
|
||||
);
|
||||
};
|
||||
|
||||
/** main component */
|
||||
export const ControlGeneralViewSelector = ({
|
||||
selector,
|
||||
selectors,
|
||||
|
@ -121,7 +221,7 @@ export const ControlGeneralViewSelector = ({
|
|||
[errorMap, index, conditionsAdded, onChange, selector, selectors]
|
||||
);
|
||||
|
||||
const onChangeCondition = useCallback(
|
||||
const onChangeStringArrayCondition = useCallback(
|
||||
(prop: string, values: string[]) => {
|
||||
const updatedSelector = { ...selector, [prop]: values };
|
||||
const errors = [];
|
||||
|
@ -156,12 +256,25 @@ export const ControlGeneralViewSelector = ({
|
|||
[errorMap, index, conditionsAdded, onChange, selector]
|
||||
);
|
||||
|
||||
const onChangeBooleanCondition = useCallback(
|
||||
(prop: string, value: boolean) => {
|
||||
const updatedSelector = { ...selector, [prop]: value };
|
||||
|
||||
onChange(updatedSelector, index);
|
||||
},
|
||||
[index, onChange, selector]
|
||||
);
|
||||
|
||||
const onAddCondition = useCallback(
|
||||
(prop: string) => {
|
||||
onChangeCondition(prop, []);
|
||||
if (prop in ControlSelectorBooleanConditions) {
|
||||
onChangeBooleanCondition(prop, true);
|
||||
} else {
|
||||
onChangeStringArrayCondition(prop, []);
|
||||
}
|
||||
closeAddCondition();
|
||||
},
|
||||
[closeAddCondition, onChangeCondition]
|
||||
[closeAddCondition, onChangeBooleanCondition, onChangeStringArrayCondition]
|
||||
);
|
||||
|
||||
const onRemoveCondition = useCallback(
|
||||
|
@ -185,10 +298,10 @@ export const ControlGeneralViewSelector = ({
|
|||
const values = selector[prop as keyof ControlSelector] as string[];
|
||||
|
||||
if (values && values.indexOf(value) === -1) {
|
||||
onChangeCondition(prop, [...values, value]);
|
||||
onChangeStringArrayCondition(prop, [...values, value]);
|
||||
}
|
||||
},
|
||||
[onChangeCondition, selector]
|
||||
[onChangeStringArrayCondition, selector]
|
||||
);
|
||||
|
||||
const errors = useMemo(() => {
|
||||
|
@ -270,56 +383,31 @@ export const ControlGeneralViewSelector = ({
|
|||
</EuiFormRow>
|
||||
{Object.keys(selector).map((prop: string) => {
|
||||
if (['name', 'hasErrors'].indexOf(prop) === -1) {
|
||||
const values = selector[prop as keyof ControlSelector] as string[];
|
||||
const selectedOptions =
|
||||
values?.map((option) => {
|
||||
return { label: option, value: option };
|
||||
}) || [];
|
||||
|
||||
const label = i18n.getConditionLabel(prop);
|
||||
const restrictedValues = ControlSelectorConditionUIOptionsMap[prop]?.values;
|
||||
|
||||
return (
|
||||
<EuiFormRow
|
||||
label={label}
|
||||
fullWidth={true}
|
||||
key={prop}
|
||||
isInvalid={!!errorMap.hasOwnProperty(prop)}
|
||||
>
|
||||
<EuiFlexGroup alignItems="center" gutterSize="m">
|
||||
<EuiFlexItem>
|
||||
<EuiComboBox
|
||||
aria-label={label}
|
||||
fullWidth={true}
|
||||
onCreateOption={
|
||||
!restrictedValues
|
||||
? (searchValue) => onAddValueToCondition(prop, searchValue)
|
||||
: undefined
|
||||
}
|
||||
selectedOptions={selectedOptions}
|
||||
options={
|
||||
restrictedValues
|
||||
? restrictedValues.map((value: string) => ({ label: value, value }))
|
||||
: selectedOptions
|
||||
}
|
||||
onChange={(options) =>
|
||||
onChangeCondition(prop, options.map((option) => option.value) as string[])
|
||||
}
|
||||
isClearable
|
||||
data-test-subj={'cloud-defend-selectorcondition-' + prop}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButtonIcon
|
||||
iconType="cross"
|
||||
onClick={() => onRemoveCondition(prop)}
|
||||
aria-label="Remove condition"
|
||||
data-test-subj={'cloud-defend-btnremovecondition-' + prop}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiFormRow>
|
||||
);
|
||||
if (prop in ControlSelectorBooleanConditions) {
|
||||
return (
|
||||
<BooleanCondition
|
||||
key={prop}
|
||||
label={label}
|
||||
prop={prop}
|
||||
onRemoveCondition={onRemoveCondition}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<StringArrayCondition
|
||||
key={prop}
|
||||
label={label}
|
||||
prop={prop}
|
||||
selector={selector}
|
||||
errorMap={errorMap}
|
||||
onAddValueToCondition={onAddValueToCondition}
|
||||
onChangeStringArrayCondition={onChangeStringArrayCondition}
|
||||
onRemoveCondition={onRemoveCondition}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
})}
|
||||
</EuiForm>
|
||||
|
@ -345,7 +433,11 @@ export const ControlGeneralViewSelector = ({
|
|||
size="s"
|
||||
items={remainingProps.map((prop) => {
|
||||
return (
|
||||
<EuiContextMenuItem key={prop} onClick={() => onAddCondition(prop)}>
|
||||
<EuiContextMenuItem
|
||||
data-test-subj={`cloud-defend-addmenu-${prop}`}
|
||||
key={prop}
|
||||
onClick={() => onAddCondition(prop)}
|
||||
>
|
||||
{i18n.getConditionLabel(prop)}
|
||||
</EuiContextMenuItem>
|
||||
);
|
||||
|
|
|
@ -0,0 +1,190 @@
|
|||
{
|
||||
"$id": "https://elastic.co/cloud-defend/policy-schema.json",
|
||||
"type": "object",
|
||||
"required": ["selectors", "responses"],
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"selectors": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"items": {
|
||||
"$ref": "#/$defs/selector"
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"items": {
|
||||
"$ref": "#/$defs/response"
|
||||
}
|
||||
}
|
||||
},
|
||||
"$defs": {
|
||||
"selector": {
|
||||
"type": "object",
|
||||
"required": ["name"],
|
||||
"additionalProperties": false,
|
||||
"anyOf": [
|
||||
{
|
||||
"required": ["operation"]
|
||||
},
|
||||
{
|
||||
"required": ["containerImageName"]
|
||||
},
|
||||
{
|
||||
"required": ["containerImageTag"]
|
||||
},
|
||||
{
|
||||
"required": ["targetFilePath"]
|
||||
},
|
||||
{
|
||||
"required": ["orchestratorClusterId"]
|
||||
},
|
||||
{
|
||||
"required": ["orchestratorClusterName"]
|
||||
},
|
||||
{
|
||||
"required": ["orchestratorNamespace"]
|
||||
},
|
||||
{
|
||||
"required": ["orchestratorResourceLabel"]
|
||||
},
|
||||
{
|
||||
"required": ["orchestratorResourceName"]
|
||||
},
|
||||
{
|
||||
"required": ["orchestratorType"]
|
||||
},
|
||||
{
|
||||
"required": ["ignoreVolumeMounts"]
|
||||
},
|
||||
{
|
||||
"required": ["ignoreVolumeFiles"]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"operation": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"items": {
|
||||
"enum": [
|
||||
"createExecutable",
|
||||
"modifyExecutable",
|
||||
"createFile",
|
||||
"modifyFile",
|
||||
"deleteFile"
|
||||
]
|
||||
}
|
||||
},
|
||||
"containerImageName": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"containerImageTag": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"targetFilePath": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"orchestratorClusterId": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"orchestratorClusterName": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"orchestratorNamespace": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"orchestratorResourceLabel": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"orchestratorResourceName": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"orchestratorType": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"items": {
|
||||
"enum": ["kubernetes"]
|
||||
}
|
||||
},
|
||||
"ignoreVolumeMounts": {
|
||||
"type": "boolean",
|
||||
"description": "Ignore all volume mounts. e.g directories, files, configMaps, secrets etc...\nNote: should not be used with ignoreVolumeFiles"
|
||||
},
|
||||
"ignoreVolumeFiles": {
|
||||
"type": "boolean",
|
||||
"description": "Ignore file mounts. e.g files, configMaps, secrets\nNote: should not be used with ignoreVolumeMounts"
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"ignoreVolumeMounts": {
|
||||
"not": {
|
||||
"required": ["ignoreVolumeFiles"]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"response": {
|
||||
"type": "object",
|
||||
"required": ["match", "actions"],
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"match": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"exclude": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"actions": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"items": {
|
||||
"enum": ["alert", "block"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -8,7 +8,13 @@ import { useMemo } from 'react';
|
|||
import yaml from 'js-yaml';
|
||||
import { setDiagnosticsOptions } from 'monaco-yaml';
|
||||
import { monaco } from '@kbn/monaco';
|
||||
import { MAX_CONDITION_VALUE_LENGTH } from '../../../common/constants';
|
||||
|
||||
/**
|
||||
* In order to keep this json in sync with https://github.com/elastic/cloud-defend/blob/main/modules/service/policy-schema.json
|
||||
* Do NOT commit edits to policy_schema.json as part of a PR. Please make the changes in the cloud-defend repo and use the
|
||||
* make push-policy-schema-kibana command to automate the creation of a PR to sync the changes.
|
||||
*/
|
||||
import policySchemaJson from './policy_schema.json';
|
||||
|
||||
const { Uri, editor } = monaco;
|
||||
|
||||
|
@ -24,8 +30,25 @@ export const useConfigModel = (configuration: string) => {
|
|||
}
|
||||
}, [configuration]);
|
||||
|
||||
// creating a string csv to avoid the next useMemo from re-running regardless of whether
|
||||
// selector names changed or not.
|
||||
const selectorNamesCSV = useMemo(
|
||||
() => json?.selectors?.map((selector: any) => selector.name).join(',') || '',
|
||||
[json?.selectors]
|
||||
);
|
||||
|
||||
return useMemo(() => {
|
||||
const selectorNames = json?.selectors?.map((selector: any) => selector.name) || [];
|
||||
const schema: any = { ...policySchemaJson };
|
||||
|
||||
// dynamically setting enum values for response match and exclude properties.
|
||||
if (schema.$defs.response.properties.match.items) {
|
||||
const responseProps = schema.$defs.response.properties;
|
||||
const selectorEnum = { enum: selectorNamesCSV.split(',') };
|
||||
responseProps.match.items = selectorEnum;
|
||||
responseProps.exclude.items = selectorEnum;
|
||||
} else {
|
||||
throw new Error('cloud_defend json schema is invalid');
|
||||
}
|
||||
|
||||
setDiagnosticsOptions({
|
||||
validate: true,
|
||||
|
@ -35,112 +58,7 @@ export const useConfigModel = (configuration: string) => {
|
|||
{
|
||||
uri: SCHEMA_URI,
|
||||
fileMatch: [String(modelUri)],
|
||||
schema: {
|
||||
type: 'object',
|
||||
required: ['selectors', 'responses'],
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
selectors: {
|
||||
type: 'array',
|
||||
minItems: 1,
|
||||
items: { $ref: '#/$defs/selector' },
|
||||
},
|
||||
responses: {
|
||||
type: 'array',
|
||||
minItems: 1,
|
||||
items: { $ref: '#/$defs/response' },
|
||||
},
|
||||
},
|
||||
$defs: {
|
||||
selector: {
|
||||
type: 'object',
|
||||
required: ['name'],
|
||||
additionalProperties: false,
|
||||
anyOf: [
|
||||
{ required: ['operation'] },
|
||||
{ required: ['containerImageName'] },
|
||||
{ required: ['containerImageTag'] },
|
||||
{ required: ['targetFilePath'] },
|
||||
{ required: ['orchestratorClusterId'] },
|
||||
{ required: ['orchestratorClusterName'] },
|
||||
{ required: ['orchestratorNamespace'] },
|
||||
{ required: ['orchestratorResourceLabel'] },
|
||||
{ required: ['orchestratorResourceName'] },
|
||||
{ required: ['orchestratorType'] },
|
||||
],
|
||||
properties: {
|
||||
name: {
|
||||
type: 'string',
|
||||
maxLength: MAX_CONDITION_VALUE_LENGTH,
|
||||
},
|
||||
operation: {
|
||||
type: 'array',
|
||||
minItems: 1,
|
||||
items: { enum: ['createExecutable', 'modifyExecutable', 'execMemFd'] },
|
||||
},
|
||||
containerImageName: {
|
||||
type: 'array',
|
||||
minItems: 1,
|
||||
items: { type: 'string', maxLength: MAX_CONDITION_VALUE_LENGTH },
|
||||
},
|
||||
containerImageTag: {
|
||||
type: 'array',
|
||||
minItems: 1,
|
||||
items: { type: 'string', maxLength: MAX_CONDITION_VALUE_LENGTH },
|
||||
},
|
||||
targetFilePath: {
|
||||
type: 'array',
|
||||
minItems: 1,
|
||||
items: { type: 'string', maxLength: MAX_CONDITION_VALUE_LENGTH },
|
||||
},
|
||||
orchestratorClusterId: {
|
||||
type: 'array',
|
||||
minItems: 1,
|
||||
items: { type: 'string', maxLength: MAX_CONDITION_VALUE_LENGTH },
|
||||
},
|
||||
orchestratorClusterName: {
|
||||
type: 'array',
|
||||
minItems: 1,
|
||||
items: { type: 'string', maxLength: MAX_CONDITION_VALUE_LENGTH },
|
||||
},
|
||||
orchestratorNamespace: {
|
||||
type: 'array',
|
||||
minItems: 1,
|
||||
items: { type: 'string', maxLength: MAX_CONDITION_VALUE_LENGTH },
|
||||
},
|
||||
orchestratorResourceLabel: {
|
||||
type: 'array',
|
||||
minItems: 1,
|
||||
items: { type: 'string', maxLength: MAX_CONDITION_VALUE_LENGTH },
|
||||
},
|
||||
orchestratorResourceName: {
|
||||
type: 'array',
|
||||
minItems: 1,
|
||||
items: { type: 'string', maxLength: MAX_CONDITION_VALUE_LENGTH },
|
||||
},
|
||||
orchestratorType: {
|
||||
type: 'array',
|
||||
minItems: 1,
|
||||
items: { enum: ['kubernetes'] },
|
||||
},
|
||||
},
|
||||
},
|
||||
response: {
|
||||
type: 'object',
|
||||
required: ['match', 'actions'],
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
match: { type: 'array', minItems: 1, items: { enum: selectorNames } },
|
||||
exclude: { type: 'array', items: { enum: selectorNames } },
|
||||
actions: {
|
||||
type: 'array',
|
||||
minItems: 1,
|
||||
items: { enum: ['alert', 'block'] },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
schema,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
@ -148,9 +66,9 @@ export const useConfigModel = (configuration: string) => {
|
|||
let model = editor.getModel(modelUri);
|
||||
|
||||
if (model === null) {
|
||||
model = editor.createModel(configuration, 'yaml', modelUri);
|
||||
model = editor.createModel('', 'yaml', modelUri);
|
||||
}
|
||||
|
||||
return model;
|
||||
}, [configuration, json?.selectors]);
|
||||
}, [selectorNamesCSV]);
|
||||
};
|
||||
|
|
|
@ -8,10 +8,10 @@
|
|||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
export const enableControl = i18n.translate('xpack.cloudDefend.enableControl', {
|
||||
defaultMessage: 'Enable BPF/LSM controls',
|
||||
defaultMessage: 'Enable drift prevention',
|
||||
});
|
||||
|
||||
export const enableControlHelp = i18n.translate('xpack.cloudDefend.enableControlHelp', {
|
||||
defaultMessage:
|
||||
'Enables BPF/LSM control mechanism, for use with FIM and container drift prevention.',
|
||||
'Toggles enablement of drift prevention policy to alert and/or block file operations.',
|
||||
});
|
||||
|
|
|
@ -30,6 +30,8 @@ export enum ControlSelectorCondition {
|
|||
containerImageName = 'containerImageName',
|
||||
containerImageTag = 'containerImageTag',
|
||||
targetFilePath = 'targetFilePath',
|
||||
ignoreVolumeFiles = 'ignoreVolumeFiles',
|
||||
ignoreVolumeMounts = 'ignoreVolumeMounts',
|
||||
orchestratorClusterId = 'orchestratorClusterId',
|
||||
orchestratorClusterName = 'orchestratorClusterName',
|
||||
orchestratorNamespace = 'orchestratorNamespace',
|
||||
|
@ -39,6 +41,11 @@ export enum ControlSelectorCondition {
|
|||
orchestratorType = 'orchestratorType',
|
||||
}
|
||||
|
||||
export enum ControlSelectorBooleanConditions {
|
||||
ignoreVolumeFiles = 'ignoreVolumeFiles',
|
||||
ignoreVolumeMounts = 'ignoreVolumeMounts',
|
||||
}
|
||||
|
||||
export enum ControlSelectorOperation {
|
||||
createExecutable = 'createExecutable',
|
||||
modifyExecutable = 'modifyExecutable',
|
||||
|
@ -66,6 +73,8 @@ export interface ControlSelector {
|
|||
containerImageName?: string[];
|
||||
containerImageTag?: string[];
|
||||
targetFilePath?: string[];
|
||||
ignoreVolumeFiles?: boolean;
|
||||
ignoreVolumeMounts?: boolean;
|
||||
orchestratorClusterId?: string[];
|
||||
orchestratorClusterName?: string[];
|
||||
orchestratorNamespace?: string[];
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
{
|
||||
"extends": "../../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "target/types",
|
||||
"outDir": "target/types"
|
||||
},
|
||||
"include": [
|
||||
"common/**/*",
|
||||
"public/**/*",
|
||||
"../../../typings/**/*"
|
||||
"../../../typings/**/*",
|
||||
"server/**/*.json",
|
||||
"public/**/*.json"
|
||||
],
|
||||
"kbn_references": [
|
||||
"@kbn/core",
|
||||
|
@ -21,6 +23,6 @@
|
|||
"@kbn/shared-ux-router"
|
||||
],
|
||||
"exclude": [
|
||||
"target/**/*",
|
||||
"target/**/*"
|
||||
]
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue