[Cloud Posture] create rules by benchmark type (#135798)

This commit is contained in:
Or Ouziel 2022-07-12 15:47:55 +03:00 committed by GitHub
parent f0bbc4044e
commit 6d3b05255b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 119 additions and 12 deletions

View file

@ -38,3 +38,14 @@ 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;

View file

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

View file

@ -32,6 +32,7 @@ const getFakeFindings = (name: string): CspFinding & { id: string } => ({
benchmark: {
name: 'CIS Kubernetes',
version: '1.6.0',
id: 'cis_k8s',
},
default_value: chance.sentence(),
description: chance.paragraph(),

View file

@ -18,6 +18,7 @@ import {
import { createPackagePolicyMock, deletePackagePolicyMock } from '@kbn/fleet-plugin/common/mocks';
import { CLOUD_SECURITY_POSTURE_PACKAGE_NAME } from '../../common/constants';
import {
getBenchmarkInputType,
onPackagePolicyPostCreateCallback,
removeCspRulesInstancesCallback,
} from './fleet_integration';
@ -41,6 +42,7 @@ describe('create CSP rules with post package create callback', () => {
benchmark: {
name: 'CIS Kubernetes V1.20',
version: 'v1.0.0',
id: 'cis_k8s',
},
enabled: true,
rego_rule_id: 'cis_1_2_2',
@ -117,4 +119,52 @@ describe('create CSP rules with post package create callback', () => {
expect(savedObjectRepositoryMock.find.mock.calls[0][0]).toMatchObject({ perPage: 10000 });
});
it('get default integration type from inputs with multiple enabled types', () => {
const mockPackagePolicy = createPackagePolicyMock();
// Both enabled falls back to default
mockPackagePolicy.inputs = [
{ type: 'cloudbeat/vanilla', enabled: true, streams: [] },
{ type: 'cloudbeat/eks', enabled: true, streams: [] },
];
const type = getBenchmarkInputType(mockPackagePolicy.inputs);
expect(type).toMatch('cis_k8s');
});
it('get default integration type from inputs without any enabled types', () => {
const mockPackagePolicy = createPackagePolicyMock();
// None enabled falls back to default
mockPackagePolicy.inputs = [
{ type: 'cloudbeat/vanilla', enabled: false, streams: [] },
{ type: 'cloudbeat/eks', enabled: false, streams: [] },
];
const type = getBenchmarkInputType(mockPackagePolicy.inputs);
expect(type).toMatch('cis_k8s');
});
it('get EKS integration type', () => {
const mockPackagePolicy = createPackagePolicyMock();
// Single EKS selected
mockPackagePolicy.inputs = [
{ type: 'cloudbeat/eks', enabled: true, streams: [] },
{ type: 'cloudbeat/vanilla', enabled: false, streams: [] },
];
const typeEks = getBenchmarkInputType(mockPackagePolicy.inputs);
expect(typeEks).toMatch('cis_eks');
});
it('get Vanilla K8S integration type', () => {
const mockPackagePolicy = createPackagePolicyMock();
// Single k8s selected
mockPackagePolicy.inputs = [
{ type: 'cloudbeat/eks', enabled: false, streams: [] },
{ type: 'cloudbeat/vanilla', enabled: true, streams: [] },
];
const typeK8s = getBenchmarkInputType(mockPackagePolicy.inputs);
expect(typeK8s).toMatch('cis_k8s');
});
});

View file

@ -12,19 +12,39 @@ import type {
SavedObjectsClientContract,
Logger,
} from '@kbn/core/server';
import { PackagePolicy, DeletePackagePoliciesResponse } from '@kbn/fleet-plugin/common';
import {
PackagePolicy,
DeletePackagePoliciesResponse,
PackagePolicyInput,
} from '@kbn/fleet-plugin/common';
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 { CspRule, CspRuleMetadata, CspRuleTemplate } from '../../common/schemas';
type ArrayElement<ArrayType extends readonly unknown[]> = ArrayType extends ReadonlyArray<
infer ElementType
>
? ElementType
: never;
type CloudbeatInputType = keyof typeof CIS_INTEGRATION_INPUTS_MAP;
type BenchmarkId = CspRuleMetadata['benchmark']['id'];
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;
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];
// Use the the default input for multiple/none selected
return CIS_INTEGRATION_INPUTS_MAP[CLOUDBEAT_VANILLA];
};
/**
* Callback to handle creation of PackagePolicies in Fleet
@ -34,14 +54,18 @@ export const onPackagePolicyPostCreateCallback = async (
packagePolicy: PackagePolicy,
savedObjectsClient: SavedObjectsClientContract
): Promise<void> => {
const benchmarkType = getBenchmarkInputType(packagePolicy.inputs);
// Create csp-rules from the generic asset
const existingRuleTemplates: SavedObjectsFindResponse<CspRuleTemplate> =
await savedObjectsClient.find({
type: CSP_RULE_TEMPLATE_SAVED_OBJECT_TYPE,
perPage: 10000,
filter: getBenchmarkTypeFilter(benchmarkType),
});
if (existingRuleTemplates.total === 0) {
logger.warn(`expected CSP rule templates to exists for type: ${benchmarkType}`);
return;
}
@ -64,7 +88,7 @@ export const onPackagePolicyPostCreateCallback = async (
* Callback to handle deletion of PackagePolicies in Fleet
*/
export const removeCspRulesInstancesCallback = async (
deletedPackagePolicy: ArrayElement<DeletePackagePoliciesResponse>,
deletedPackagePolicy: DeletePackagePoliciesResponse[number],
soClient: ISavedObjectsRepository,
logger: Logger
): Promise<void> => {

View file

@ -42,5 +42,20 @@ export const cspRuleSavedObjectMapping: SavedObjectsTypeMappingDefinition = {
export const cspRuleTemplateSavedObjectMapping: SavedObjectsTypeMappingDefinition = {
dynamic: false,
properties: {},
properties: {
metadata: {
type: 'object',
properties: {
benchmark: {
type: 'object',
properties: {
id: {
// Needed for filtering rule templates by benchmark.id
type: 'keyword',
},
},
},
},
},
},
};

View file

@ -17,7 +17,7 @@ function migrateCspRuleMetadata(
context: SavedObjectMigrationContext
): SavedObjectUnsanitizedDoc<CspRuleV840> {
// eslint-disable-next-line @typescript-eslint/naming-convention
const { enabled, muted, package_policy_id, policy_id, ...metadata } = doc.attributes;
const { enabled, muted, package_policy_id, policy_id, benchmark, ...metadata } = doc.attributes;
return {
...doc,
@ -28,6 +28,7 @@ function migrateCspRuleMetadata(
policy_id,
metadata: {
...metadata,
benchmark: { ...benchmark, id: 'cis_k8s' },
impact: metadata.impact || undefined,
default_value: metadata.default_value || undefined,
references: metadata.references || undefined,

View file

@ -19,7 +19,7 @@ function migrateCspRuleMetadata(
doc: SavedObjectUnsanitizedDoc<CspRuleTemplateV830>,
context: SavedObjectMigrationContext
): SavedObjectUnsanitizedDoc<CspRuleTemplateV840> {
const { enabled, muted, ...metadata } = doc.attributes;
const { enabled, muted, benchmark, ...metadata } = doc.attributes;
return {
...doc,
attributes: {
@ -27,6 +27,7 @@ function migrateCspRuleMetadata(
muted,
metadata: {
...metadata,
benchmark: { ...benchmark, id: 'cis_k8s' },
impact: metadata.impact || undefined,
default_value: metadata.default_value || undefined,
references: metadata.references || undefined,