[Cloud Posture] Rename and modify dataYml to runtimeCfg (#138248)

This commit is contained in:
Uri Weisman 2022-08-14 19:05:21 +03:00 committed by GitHub
parent c54d91f1a4
commit cb5f9d5d12
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 69 additions and 78 deletions

View file

@ -43,13 +43,5 @@ export const INTERNAL_FEATURE_FLAGS = {
export const CSP_RULE_SAVED_OBJECT_TYPE = 'csp_rule';
export const CSP_RULE_TEMPLATE_SAVED_OBJECT_TYPE = 'csp-rule-template';
export const CLOUDBEAT_VANILLA = 'cloudbeat/vanilla'; // Integration input
export const INTEGRATION_CIS_K8S = 'cis_k8s'; // rule template benchmark id
export const CLOUDBEAT_EKS = 'cloudbeat/eks'; // Integration input
export const INTEGRATION_CIS_EKS = 'cis_eks'; // rule template benchmark id
export const CIS_INTEGRATION_INPUTS_MAP = {
[CLOUDBEAT_VANILLA]: INTEGRATION_CIS_K8S,
[CLOUDBEAT_EKS]: INTEGRATION_CIS_EKS,
} as const;
export const CLOUDBEAT_VANILLA = 'cloudbeat/cis_k8s'; // Integration input
export const CLOUDBEAT_EKS = 'cloudbeat/cis_eks'; // Integration input

View file

@ -6,13 +6,11 @@
*/
import { schema as rt, TypeOf } from '@kbn/config-schema';
export const benchmarkIdSchema = rt.oneOf([rt.literal('cis_k8s'), rt.literal('cis_eks')]);
export const cspRuleMetadataSchema = rt.object({
audit: rt.string(),
benchmark: rt.object({
name: rt.string(),
id: benchmarkIdSchema,
id: rt.string(),
version: rt.string(),
}),
default_value: rt.maybe(rt.string()),

View file

@ -5,12 +5,11 @@
* 2.0.
*/
import { schema as rt, TypeOf } from '@kbn/config-schema';
import { benchmarkIdSchema } from './csp_rule_metadata';
// cspRulesConfigSchema has to match the 'DataYaml' struct in https://github.com/elastic/cloudbeat/blob/main/config/config.go#L45-L51
// cspRulesConfigSchema has to match the 'RuntimeCfg' struct in https://github.com/elastic/cloudbeat/blob/main/config/config.go#L45-L51
export const cspRulesConfigSchema = rt.object({
data_yaml: rt.object({
activated_rules: rt.recordOf(benchmarkIdSchema, rt.arrayOf(rt.string())),
runtime_cfg: rt.object({
activated_rules: rt.recordOf(rt.string(), rt.arrayOf(rt.string())),
}),
});

View file

@ -17,9 +17,9 @@ import {
} from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n-react';
import { i18n } from '@kbn/i18n';
import { CIS_INTEGRATION_INPUTS_MAP } from '../../../common/constants';
import { CLOUDBEAT_EKS, CLOUDBEAT_VANILLA } from '../../../common/constants';
export type InputType = keyof typeof CIS_INTEGRATION_INPUTS_MAP;
export type InputType = typeof CLOUDBEAT_EKS | typeof CLOUDBEAT_VANILLA;
interface Props {
type: InputType;
@ -29,14 +29,14 @@ interface Props {
const kubeDeployOptions: Array<EuiComboBoxOptionOption<InputType>> = [
{
value: 'cloudbeat/vanilla',
value: CLOUDBEAT_VANILLA,
label: i18n.translate(
'xpack.csp.createPackagePolicy.stepConfigure.integrationSettingsSection.vanillaKubernetesDeploymentOption',
{ defaultMessage: 'Unmanaged Kubernetes' }
),
},
{
value: 'cloudbeat/eks',
value: CLOUDBEAT_EKS,
label: i18n.translate(
'xpack.csp.createPackagePolicy.stepConfigure.integrationSettingsSection.eksKubernetesDeploymentOption',
{ defaultMessage: 'EKS (Elastic Kubernetes Service)' }

View file

@ -7,6 +7,7 @@
import type { NewPackagePolicy } from '@kbn/fleet-plugin/public';
import type { PackagePolicy } from '@kbn/fleet-plugin/common';
import { BenchmarkId } from '../../../common/types';
import { CLOUDBEAT_EKS, CLOUDBEAT_VANILLA } from '../../../common/constants';
export const getCspNewPolicyMock = (type: BenchmarkId = 'cis_k8s'): NewPackagePolicy => ({
name: 'some-cloud_security_posture-policy',
@ -17,7 +18,7 @@ export const getCspNewPolicyMock = (type: BenchmarkId = 'cis_k8s'): NewPackagePo
output_id: '',
inputs: [
{
type: 'cloudbeat/vanilla',
type: CLOUDBEAT_VANILLA,
policy_template: 'kspm',
enabled: type === 'cis_k8s',
streams: [
@ -31,7 +32,7 @@ export const getCspNewPolicyMock = (type: BenchmarkId = 'cis_k8s'): NewPackagePo
],
},
{
type: 'cloudbeat/eks',
type: CLOUDBEAT_EKS,
policy_template: 'kspm',
enabled: type === 'cis_eks',
streams: [
@ -62,7 +63,7 @@ export const getCspNewPolicyMock = (type: BenchmarkId = 'cis_k8s'): NewPackagePo
version: '0.0.21',
},
vars: {
dataYaml: {
runtimeCfg: {
type: 'yaml',
},
},
@ -100,7 +101,7 @@ export const getCspPolicyMock = (type: BenchmarkId = 'cis_k8s'): PackagePolicy =
enabled: true,
},
],
type: 'cloudbeat/vanilla',
type: CLOUDBEAT_VANILLA,
enabled: type === 'cis_k8s',
},
{
@ -126,7 +127,7 @@ export const getCspPolicyMock = (type: BenchmarkId = 'cis_k8s'): PackagePolicy =
enabled: false,
},
],
type: 'cloudbeat/eks',
type: CLOUDBEAT_EKS,
enabled: type === 'cis_eks',
},
],

View file

@ -125,8 +125,8 @@ describe('create CSP rules with post package create callback', () => {
// Both enabled falls back to default
mockPackagePolicy.inputs = [
{ type: 'cloudbeat/vanilla', enabled: true, streams: [] },
{ type: 'cloudbeat/eks', enabled: true, streams: [] },
{ type: 'cloudbeat/cis_k8s', enabled: true, streams: [] },
{ type: 'cloudbeat/cis_eks', enabled: true, streams: [] },
];
const type = getBenchmarkInputType(mockPackagePolicy.inputs);
expect(type).toMatch('cis_k8s');
@ -137,8 +137,8 @@ describe('create CSP rules with post package create callback', () => {
// None enabled falls back to default
mockPackagePolicy.inputs = [
{ type: 'cloudbeat/vanilla', enabled: false, streams: [] },
{ type: 'cloudbeat/eks', enabled: false, streams: [] },
{ type: 'cloudbeat/cis_k8s', enabled: false, streams: [] },
{ type: 'cloudbeat/cis_eks', enabled: false, streams: [] },
];
const type = getBenchmarkInputType(mockPackagePolicy.inputs);
expect(type).toMatch('cis_k8s');
@ -149,8 +149,8 @@ describe('create CSP rules with post package create callback', () => {
// Single EKS selected
mockPackagePolicy.inputs = [
{ type: 'cloudbeat/eks', enabled: true, streams: [] },
{ type: 'cloudbeat/vanilla', enabled: false, streams: [] },
{ type: 'cloudbeat/cis_eks', enabled: true, streams: [] },
{ type: 'cloudbeat/cis_k8s', enabled: false, streams: [] },
];
const typeEks = getBenchmarkInputType(mockPackagePolicy.inputs);
expect(typeEks).toMatch('cis_eks');
@ -161,8 +161,8 @@ describe('create CSP rules with post package create callback', () => {
// Single k8s selected
mockPackagePolicy.inputs = [
{ type: 'cloudbeat/eks', enabled: false, streams: [] },
{ type: 'cloudbeat/vanilla', enabled: true, streams: [] },
{ type: 'cloudbeat/cis_eks', enabled: false, streams: [] },
{ type: 'cloudbeat/cis_k8s', enabled: true, streams: [] },
];
const typeK8s = getBenchmarkInputType(mockPackagePolicy.inputs);
expect(typeK8s).toMatch('cis_k8s');

View file

@ -20,30 +20,27 @@ import {
import { createCspRuleSearchFilterByPackagePolicy } from '../../common/utils/helpers';
import {
CLOUDBEAT_VANILLA,
CIS_INTEGRATION_INPUTS_MAP,
CSP_RULE_SAVED_OBJECT_TYPE,
CSP_RULE_TEMPLATE_SAVED_OBJECT_TYPE,
} from '../../common/constants';
import type { CspRule, CspRuleTemplate } from '../../common/schemas';
import type { BenchmarkId } from '../../common/types';
type CloudbeatInputType = keyof typeof CIS_INTEGRATION_INPUTS_MAP;
const getBenchmarkTypeFilter = (type: BenchmarkId): string =>
`${CSP_RULE_TEMPLATE_SAVED_OBJECT_TYPE}.attributes.metadata.benchmark.id: "${type}"`;
const isEnabledBenchmarkInputType = (input: PackagePolicyInput) =>
input.type in CIS_INTEGRATION_INPUTS_MAP && !!input.enabled;
const isEnabledBenchmarkInputType = (input: PackagePolicyInput) => !!input.type && input.enabled;
export const getBenchmarkInputType = (inputs: PackagePolicy['inputs']): BenchmarkId => {
const enabledInputs = inputs.filter(isEnabledBenchmarkInputType);
// Use the only enabled input
if (enabledInputs.length === 1)
return CIS_INTEGRATION_INPUTS_MAP[enabledInputs[0].type as CloudbeatInputType];
if (enabledInputs.length === 1) {
return getInputType(enabledInputs[0].type);
}
// Use the the default input for multiple/none selected
return CIS_INTEGRATION_INPUTS_MAP[CLOUDBEAT_VANILLA];
// Use the default benchmark id for multiple/none selected
return getInputType(CLOUDBEAT_VANILLA);
};
/**
@ -142,3 +139,8 @@ const generateRulesFromTemplates = (
policy_id: policyId,
},
}));
const getInputType = (inputType: string): string => {
// Get the last part of the input type, input type structure: cloudbeat/<benchmark_id>
return inputType.split('/')[1];
};

View file

@ -241,7 +241,7 @@ describe('Cloud Security Posture Plugin', () => {
const packageMock = createPackagePolicyMock();
packageMock.package!.name = CLOUD_SECURITY_POSTURE_PACKAGE_NAME;
packageMock.vars = { dataYaml: { type: 'foo' } };
packageMock.vars = { runtimeCfg: { type: 'foo' } };
const packagePolicyPostCreateCallbacks: PostPackagePolicyPostCreateCallback[] = [];
fleetMock.registerExternalCallback.mockImplementation((...args) => {
@ -272,8 +272,8 @@ describe('Cloud Security Posture Plugin', () => {
);
if (fleetMock.packagePolicyService.update.mock.calls.length) {
expect(updatedPackagePolicy).toHaveProperty('vars');
expect(updatedPackagePolicy.vars).toHaveProperty('dataYaml');
expect(updatedPackagePolicy.vars!.dataYaml).toHaveProperty('value');
expect(updatedPackagePolicy.vars).toHaveProperty('runtimeCfg');
expect(updatedPackagePolicy.vars!.runtimeCfg).toHaveProperty('value');
}
}
expect(fleetMock.packagePolicyService.update).toHaveBeenCalledTimes(1);

View file

@ -134,7 +134,7 @@ describe('Update rules configuration API', () => {
};
const cspConfig = await createRulesConfig(cspRules as SavedObjectsFindResponse<CspRule>);
expect(cspConfig).toMatchObject({
data_yaml: { activated_rules: { cis_k8s: ['cis_1_1_1', 'cis_1_1_3'] } },
runtime_cfg: { activated_rules: { cis_k8s: ['cis_1_1_1', 'cis_1_1_3'] } },
});
});
@ -177,24 +177,24 @@ describe('Update rules configuration API', () => {
],
};
const cspConfig = await createRulesConfig(cspRules as SavedObjectsFindResponse<CspRule>);
expect(cspConfig).toMatchObject({ data_yaml: { activated_rules: { cis_k8s: [] } } });
expect(cspConfig).toMatchObject({ runtime_cfg: { activated_rules: {} } });
});
it('validate adding new dataYaml to package policy instance', async () => {
it('validate adding new runtimeCfg to package policy instance', async () => {
const packagePolicy = createPackagePolicyMock();
packagePolicy.vars = { dataYaml: { type: 'yaml' } };
packagePolicy.vars = { runtimeCfg: { type: 'yaml' } };
const dataYaml = 'data_yaml:\n activated_rules:\n cis_k8s:\n - 1.1.1\n - 1.1.2\n';
const updatedPackagePolicy = setVarToPackagePolicy(packagePolicy, dataYaml);
expect(updatedPackagePolicy.vars).toEqual({ dataYaml: { type: 'yaml', value: dataYaml } });
const runtimeCfg = 'runtime_cfg:\n activated_rules:\n cis_k8s:\n - 1.1.1\n - 1.1.2\n';
const updatedPackagePolicy = setVarToPackagePolicy(packagePolicy, runtimeCfg);
expect(updatedPackagePolicy.vars).toEqual({ runtimeCfg: { type: 'yaml', value: runtimeCfg } });
});
it('validate adding new dataYaml to package policy instance when it not exists on source', async () => {
it('validate adding new runtimeCfg to package policy instance when it not exists on source', async () => {
const packagePolicy = createPackagePolicyMock();
const dataYaml = 'data_yaml:\n activated_rules:\n cis_k8s:\n - 1.1.1\n - 1.1.2\n';
const updatedPackagePolicy = setVarToPackagePolicy(packagePolicy, dataYaml);
expect(updatedPackagePolicy.vars).toEqual({ dataYaml: { type: 'yaml', value: dataYaml } });
const runtimeCfg = 'runtime_cfg:\n activated_rules:\n cis_k8s:\n - 1.1.1\n - 1.1.2\n';
const updatedPackagePolicy = setVarToPackagePolicy(packagePolicy, runtimeCfg);
expect(updatedPackagePolicy.vars).toEqual({ runtimeCfg: { type: 'yaml', value: runtimeCfg } });
});
it('verify that the API for updating package policy was invoked', async () => {
@ -254,7 +254,7 @@ describe('Update rules configuration API', () => {
mockSoClient.find.mockResolvedValueOnce(cspRules as SavedObjectsFindResponse<CspRule>);
const mockPackagePolicy = createPackagePolicyMock();
mockPackagePolicy.vars = { dataYaml: { type: 'foo' } };
mockPackagePolicy.vars = { runtimeCfg: { type: 'foo' } };
const packagePolicyId1 = chance.guid();
mockPackagePolicy.id = packagePolicyId1;
@ -268,8 +268,8 @@ describe('Update rules configuration API', () => {
user
);
expect(updatePackagePolicy.vars!.dataYaml).toHaveProperty('value');
expect(updatePackagePolicy.vars!.dataYaml).toMatchObject({ type: 'yaml' });
expect(updatePackagePolicy.vars!.runtimeCfg).toHaveProperty('value');
expect(updatePackagePolicy.vars!.runtimeCfg).toMatchObject({ type: 'yaml' });
expect(mockPackagePolicyService.update).toBeCalledTimes(1);
expect(mockPackagePolicyService.update.mock.calls[0][2]).toEqual(packagePolicyId1);
});
@ -322,7 +322,7 @@ describe('Update rules configuration API', () => {
const packagePolicyId1 = chance.guid();
const user = null;
mockPackagePolicy.id = packagePolicyId1;
mockPackagePolicy.vars = { foo: {}, dataYaml: { type: 'yaml' } };
mockPackagePolicy.vars = { foo: {}, runtimeCfg: { type: 'yaml' } };
mockPackagePolicyService.update.mockImplementation(
(
@ -375,7 +375,7 @@ describe('Update rules configuration API', () => {
};
mockSoClient.find.mockResolvedValueOnce(cspRules as SavedObjectsFindResponse<CspRule>);
mockPackagePolicy.vars = { dataYaml: { type: 'yaml' } };
mockPackagePolicy.vars = { runtimeCfg: { type: 'yaml' } };
await updateAgentConfiguration(
mockPackagePolicyService,

View file

@ -66,22 +66,21 @@ export const getCspRules = (
};
const getEnabledRulesByBenchmark = (rules: SavedObjectsFindResponse<CspRule>['saved_objects']) =>
rules.reduce<CspRulesConfiguration['data_yaml']['activated_rules']>(
(benchmarks, rule) => {
const benchmark = benchmarks[rule.attributes.metadata.benchmark.id];
if (!rule.attributes.enabled || !benchmark) return benchmarks;
rules.reduce<CspRulesConfiguration['runtime_cfg']['activated_rules']>((benchmarks, rule) => {
const benchmark = rule.attributes.metadata.benchmark.id;
if (!rule.attributes.enabled || !benchmark) return benchmarks;
if (!benchmarks[benchmark]) {
benchmarks[benchmark] = [];
}
benchmark.push(rule.attributes.metadata.rego_rule_id);
return benchmarks;
},
{ cis_k8s: [], cis_eks: [] }
);
benchmarks[benchmark].push(rule.attributes.metadata.rego_rule_id);
return benchmarks;
}, {});
export const createRulesConfig = (
cspRules: SavedObjectsFindResponse<CspRule>
): CspRulesConfiguration => ({
data_yaml: {
runtime_cfg: {
activated_rules: getEnabledRulesByBenchmark(cspRules.saved_objects),
},
});
@ -92,15 +91,15 @@ export const convertRulesConfigToYaml = (config: CspRulesConfiguration): string
export const setVarToPackagePolicy = (
packagePolicy: PackagePolicy,
dataYaml: string
runtimeCfg: string
): PackagePolicy => {
const configFile: PackagePolicyConfigRecord = {
dataYaml: { type: 'yaml', value: dataYaml },
runtimeCfg: { type: 'yaml', value: runtimeCfg },
};
const updatedPackagePolicy = produce(packagePolicy, (draft) => {
unset(draft, 'id');
if (draft.vars) {
draft.vars.dataYaml = configFile.dataYaml;
draft.vars.runtimeCfg = configFile.runtimeCfg;
} else {
draft.vars = configFile;
}
@ -117,8 +116,8 @@ export const updateAgentConfiguration = async (
): Promise<PackagePolicy> => {
const cspRules = await getCspRules(soClient, packagePolicy);
const rulesConfig = createRulesConfig(cspRules);
const dataYaml = convertRulesConfigToYaml(rulesConfig);
const updatedPackagePolicy = setVarToPackagePolicy(packagePolicy, dataYaml);
const runtimeCfg = convertRulesConfigToYaml(rulesConfig);
const updatedPackagePolicy = setVarToPackagePolicy(packagePolicy, runtimeCfg);
const options = { user: user ? user : undefined };
return packagePolicyService.update(
soClient,