[8.x] [Security Solution] Fix prebuilt rules force upgrade on Endpoint policy creation (#217959) (#218154)

# Backport

This will backport the following commits from `main` to `8.x`:
- [[Security Solution] Fix prebuilt rules force upgrade on Endpoint
policy creation
(#217959)](https://github.com/elastic/kibana/pull/217959)

<!--- Backport version: 9.6.6 -->

### Questions ?
Please refer to the [Backport tool
documentation](https://github.com/sorenlouv/backport)

<!--BACKPORT [{"author":{"name":"Dmitrii
Shevchenko","email":"dmitrii.shevchenko@elastic.co"},"sourceCommit":{"committedDate":"2025-04-14T17:22:38Z","message":"[Security
Solution] Fix prebuilt rules force upgrade on Endpoint policy creation
(#217959)\n\n**Resolves:
https://github.com/elastic/security-team/issues/7216**\n\n##
Summary\n\nThis PR updates the Endpoint policy callback to: \n\n-
**Install only the Elastic Defend rule if it's missing**,
without\nupgrading it to the latest version. Previously, the rule was
both\ninstalled and updated whenever an Endpoint policy was created,
which\nconflicted with rule customization. Automatic upgrades could
erase\nexisting user customizations.\n\n- **Avoid triggering the
installation or upgrade of any other prebuilt\nrules** as part of this
flow. The Endpoint package policy
creation\ncallback\n([source](f7d8bc3c25/x-pack/solutions/security/plugins/security_solution/server/fleet_integration/fleet_integration.ts (L181-L187)))\npreviously
installed and upgraded **all** prebuilt detection rules to\ntheir target
versions whenever an Endpoint policy was created.\n\nThis logic relied
on the legacy rule upgrade method, which has a known\nissue that causes
all configured rule actions and exceptions to be lost.\nBy removing the
upgrade logic, this PR eliminates that
incorrect\nbehavior.","sha":"9f5425f061e6fff8579f7db1117af075b1b6ca1b","branchLabelMapping":{"^v9.1.0$":"main","^v8.19.0$":"8.x","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["bug","release_note:fix","impact:high","Feature:Endpoint","Team:Detections
and Resp","Team: SecuritySolution","Team:Detection Rule
Management","Feature:Prebuilt Detection
Rules","backport:version","v9.1.0","v8.19.0","v8.18.1","v9.0.1","v8.17.5"],"title":"[Security
Solution] Fix prebuilt rules force upgrade on Endpoint policy
creation","number":217959,"url":"https://github.com/elastic/kibana/pull/217959","mergeCommit":{"message":"[Security
Solution] Fix prebuilt rules force upgrade on Endpoint policy creation
(#217959)\n\n**Resolves:
https://github.com/elastic/security-team/issues/7216**\n\n##
Summary\n\nThis PR updates the Endpoint policy callback to: \n\n-
**Install only the Elastic Defend rule if it's missing**,
without\nupgrading it to the latest version. Previously, the rule was
both\ninstalled and updated whenever an Endpoint policy was created,
which\nconflicted with rule customization. Automatic upgrades could
erase\nexisting user customizations.\n\n- **Avoid triggering the
installation or upgrade of any other prebuilt\nrules** as part of this
flow. The Endpoint package policy
creation\ncallback\n([source](f7d8bc3c25/x-pack/solutions/security/plugins/security_solution/server/fleet_integration/fleet_integration.ts (L181-L187)))\npreviously
installed and upgraded **all** prebuilt detection rules to\ntheir target
versions whenever an Endpoint policy was created.\n\nThis logic relied
on the legacy rule upgrade method, which has a known\nissue that causes
all configured rule actions and exceptions to be lost.\nBy removing the
upgrade logic, this PR eliminates that
incorrect\nbehavior.","sha":"9f5425f061e6fff8579f7db1117af075b1b6ca1b"}},"sourceBranch":"main","suggestedTargetBranches":["8.x","8.18","9.0","8.17"],"targetPullRequestStates":[{"branch":"main","label":"v9.1.0","branchLabelMappingKey":"^v9.1.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/217959","number":217959,"mergeCommit":{"message":"[Security
Solution] Fix prebuilt rules force upgrade on Endpoint policy creation
(#217959)\n\n**Resolves:
https://github.com/elastic/security-team/issues/7216**\n\n##
Summary\n\nThis PR updates the Endpoint policy callback to: \n\n-
**Install only the Elastic Defend rule if it's missing**,
without\nupgrading it to the latest version. Previously, the rule was
both\ninstalled and updated whenever an Endpoint policy was created,
which\nconflicted with rule customization. Automatic upgrades could
erase\nexisting user customizations.\n\n- **Avoid triggering the
installation or upgrade of any other prebuilt\nrules** as part of this
flow. The Endpoint package policy
creation\ncallback\n([source](f7d8bc3c25/x-pack/solutions/security/plugins/security_solution/server/fleet_integration/fleet_integration.ts (L181-L187)))\npreviously
installed and upgraded **all** prebuilt detection rules to\ntheir target
versions whenever an Endpoint policy was created.\n\nThis logic relied
on the legacy rule upgrade method, which has a known\nissue that causes
all configured rule actions and exceptions to be lost.\nBy removing the
upgrade logic, this PR eliminates that
incorrect\nbehavior.","sha":"9f5425f061e6fff8579f7db1117af075b1b6ca1b"}},{"branch":"8.x","label":"v8.19.0","branchLabelMappingKey":"^v8.19.0$","isSourceBranch":false,"state":"NOT_CREATED"},{"branch":"8.18","label":"v8.18.1","branchLabelMappingKey":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"},{"branch":"9.0","label":"v9.0.1","branchLabelMappingKey":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"},{"branch":"8.17","label":"v8.17.5","branchLabelMappingKey":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"}]}]
BACKPORT-->

Co-authored-by: Dmitrii Shevchenko <dmitrii.shevchenko@elastic.co>
This commit is contained in:
Kibana Machine 2025-04-14 21:09:57 +02:00 committed by GitHub
parent 0ab217af55
commit 48554f91e1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 92 additions and 68 deletions

View file

@ -185,7 +185,6 @@ export class EndpointAppContextService {
this.setupDependencies.securitySolutionRequestContextFactory,
alerting,
licenseService,
exceptionListsClient,
this.setupDependencies.cloud,
productFeaturesService,
telemetryConfigProvider

View file

@ -191,7 +191,6 @@ describe('Fleet integrations', () => {
requestContextFactoryMock.create(),
endpointAppContextStartContract.alerting,
licenseService,
exceptionListClient,
cloudService,
productFeaturesService,
telemetryConfigProviderMock

View file

@ -47,7 +47,7 @@ import type { NewPolicyData, PolicyConfig } from '../../common/endpoint/types';
import type { LicenseService } from '../../common/license';
import type { ManifestManager } from '../endpoint/services';
import type { IRequestContextFactory } from '../request_context_factory';
import { installPrepackagedRules } from './handlers/install_prepackaged_rules';
import { installEndpointSecurityPrebuiltRule } from '../lib/detection_engine/prebuilt_rules/logic/rules_package/install_endpoint_security_prebuilt_rule';
import { createPolicyArtifactManifest } from './handlers/create_policy_artifact_manifest';
import { createDefaultPolicy } from './handlers/create_default_policy';
import { validatePolicyAgainstLicense } from './handlers/validate_policy_against_license';
@ -122,7 +122,6 @@ export const getPackagePolicyCreateCallback = (
securitySolutionRequestContextFactory: IRequestContextFactory,
alerts: AlertingServerStart,
licenseService: LicenseService,
exceptionsClient: ExceptionListClient | undefined,
cloud: CloudSetup,
productFeatures: ProductFeaturesService,
telemetryConfigProvider: TelemetryConfigProvider
@ -176,15 +175,13 @@ export const getPackagePolicyCreateCallback = (
// perform these operations in parallel in order to help in not delaying the API response too much
const [, manifestValue] = await Promise.all([
// Install Detection Engine prepackaged rules
exceptionsClient &&
installPrepackagedRules({
logger,
context: securitySolutionContext,
request,
alerts,
exceptionsClient,
}),
installEndpointSecurityPrebuiltRule({
logger,
context: securitySolutionContext,
request,
alerts,
soClient,
}),
// create the Artifact Manifest for this policy
createPolicyArtifactManifest(logger, manifestManager),

View file

@ -1,55 +0,0 @@
/*
* 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 type { KibanaRequest, Logger } from '@kbn/core/server';
import type { ExceptionListClient } from '@kbn/lists-plugin/server';
import type { AlertingServerStart } from '@kbn/alerting-plugin/server';
import { createDetectionIndex } from '../../lib/detection_engine/routes/index/create_index_route';
import { createPrepackagedRules } from '../../lib/detection_engine/prebuilt_rules';
import type { SecuritySolutionApiRequestHandlerContext } from '../../types';
export interface InstallPrepackagedRulesProps {
logger: Logger;
context: SecuritySolutionApiRequestHandlerContext;
request: KibanaRequest;
alerts: AlertingServerStart;
exceptionsClient: ExceptionListClient;
}
/**
* As part of a user taking advantage of Endpoint Security from within fleet, we attempt to install
* the pre-packaged rules from the detection engine, which includes an Endpoint Rule enabled by default
*/
export const installPrepackagedRules = async ({
logger,
context,
request,
alerts,
exceptionsClient,
}: InstallPrepackagedRulesProps): Promise<void> => {
// Create detection index & rules (if necessary). move past any failure, this is just a convenience
try {
await createDetectionIndex(context);
} catch (err) {
if (err.statusCode !== 409) {
// 409 -> detection index already exists, which is fine
logger.warn(
`Possible problem creating detection signals index (${err.statusCode}): ${err.message}`
);
}
}
try {
// this checks to make sure index exists first, safe to try in case of failure above
// may be able to recover from minor errors
const rulesClient = await alerts.getRulesClientWithRequest(request);
await createPrepackagedRules(context, rulesClient, exceptionsClient);
} catch (err) {
logger.error(
`Unable to create detection rules automatically (${err.statusCode}): ${err.message}`
);
}
};

View file

@ -0,0 +1,84 @@
/*
* 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 type { KibanaRequest, Logger, SavedObjectsClientContract } from '@kbn/core/server';
import type { AlertingServerStart } from '@kbn/alerting-plugin/server';
import { createDetectionIndex } from '../../../routes/index/create_index_route';
import type { SecuritySolutionApiRequestHandlerContext } from '../../../../../types';
import { ELASTIC_SECURITY_RULE_ID } from '../../../../../../common';
import { createPrebuiltRuleObjectsClient } from '../rule_objects/prebuilt_rule_objects_client';
import { createPrebuiltRuleAssetsClient } from '../rule_assets/prebuilt_rule_assets_client';
import { createPrebuiltRules } from '../rule_objects/create_prebuilt_rules';
export interface InstallEndpointSecurityPrebuiltRuleProps {
logger: Logger;
context: SecuritySolutionApiRequestHandlerContext;
request: KibanaRequest;
alerts: AlertingServerStart;
soClient: SavedObjectsClientContract;
}
/**
* As part of a user taking advantage of the Elastic Defend (formerly Endpoint
* Security) integration from within fleet, we attempt to install the `Endpoint
* Security (Elastic Defend)` prebuilt rule which will be enabled by default.
*/
export const installEndpointSecurityPrebuiltRule = async ({
logger,
context,
request,
alerts,
soClient,
}: InstallEndpointSecurityPrebuiltRuleProps): Promise<void> => {
// Create detection index & rules (if necessary). move past any failure, this is just a convenience
try {
await createDetectionIndex(context);
} catch (err) {
if (err.statusCode !== 409) {
// 409 -> detection index already exists, which is fine
logger.warn(
`Possible problem creating detection signals index (${err.statusCode}): ${err.message}`
);
}
}
try {
const rulesClient = await alerts.getRulesClientWithRequest(request);
const detectionRulesClient = context.getDetectionRulesClient();
const ruleAssetsClient = createPrebuiltRuleAssetsClient(soClient);
const ruleObjectsClient = createPrebuiltRuleObjectsClient(rulesClient);
const exceptionsListClient = context.getExceptionListClient();
const elasticDefendRule = await ruleObjectsClient.fetchInstalledRulesByIds({
ruleIds: [ELASTIC_SECURITY_RULE_ID],
});
if (elasticDefendRule.length > 0) {
// Elastic Defend rule already installed
return;
}
// Elastic Defend rule not installed, find the latest version in the
// prebuilt rule assets and install it
// This will create the endpoint list if it does not exist yet
await exceptionsListClient?.createEndpointList();
const latestRuleVersion = await ruleAssetsClient.fetchLatestVersions([
ELASTIC_SECURITY_RULE_ID,
]);
if (latestRuleVersion.length === 0) {
logger.error(
`Unable to find Elastic Defend rule in the prebuilt rule assets (rule_id: ${ELASTIC_SECURITY_RULE_ID})`
);
return;
}
const ruleAssetsToInstall = await ruleAssetsClient.fetchAssetsByVersion(latestRuleVersion);
await createPrebuiltRules(detectionRulesClient, ruleAssetsToInstall);
} catch (err) {
logger.error(
`Unable to create Endpoint Security rule automatically (${err.statusCode}): ${err.message}`
);
}
};