mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
[Cloud Posture] create rules by benchmark type (#135798)
This commit is contained in:
parent
f0bbc4044e
commit
6d3b05255b
8 changed files with 119 additions and 12 deletions
|
@ -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;
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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> => {
|
||||
|
|
|
@ -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',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue