mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
[Security Solution] Move policy meta updates to policy update (#155462)
## Summary Moving the new meta fields in Policy to update when the Policy update callback is called. These fields are used in telemetry. These fields are being moved from the Policy watcher to avoid triggering a policy deploy on many Agents at once on upgrade. Instead, these fields will be updated whenever the next Endpoint policy update comes. New Policies will have the telemetry fields already populated. ### Checklist - [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 --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
9eee24f7bf
commit
b5c88d90ce
6 changed files with 100 additions and 36 deletions
|
@ -125,7 +125,8 @@ export class EndpointAppContextService {
|
|||
logger,
|
||||
licenseService,
|
||||
featureUsageService,
|
||||
endpointMetadataService
|
||||
endpointMetadataService,
|
||||
cloud
|
||||
)
|
||||
);
|
||||
|
||||
|
|
|
@ -16,7 +16,6 @@ import { createPackagePolicyServiceMock } from '@kbn/fleet-plugin/server/mocks';
|
|||
import { PolicyWatcher } from './license_watch';
|
||||
import type { ILicense } from '@kbn/licensing-plugin/common/types';
|
||||
import { licenseMock } from '@kbn/licensing-plugin/common/licensing.mock';
|
||||
import { cloudMock } from '@kbn/cloud-plugin/server/mocks';
|
||||
import type { PackagePolicyClient } from '@kbn/fleet-plugin/server';
|
||||
import type { PackagePolicy } from '@kbn/fleet-plugin/common';
|
||||
import { createPackagePolicyMock } from '@kbn/fleet-plugin/common/mocks';
|
||||
|
@ -36,7 +35,6 @@ const MockPPWithEndpointPolicy = (cb?: (p: PolicyConfig) => PolicyConfig): Packa
|
|||
|
||||
describe('Policy-Changing license watcher', () => {
|
||||
const logger = loggingSystemMock.create().get('license_watch.test');
|
||||
const cloudServiceMock = cloudMock.createSetup();
|
||||
const soStartMock = savedObjectsServiceMock.createStartContract();
|
||||
const esStartMock = elasticsearchServiceMock.createStart();
|
||||
let packagePolicySvcMock: jest.Mocked<PackagePolicyClient>;
|
||||
|
@ -53,13 +51,7 @@ describe('Policy-Changing license watcher', () => {
|
|||
// mock a license-changing service to test reactivity
|
||||
const licenseEmitter: Subject<ILicense> = new Subject();
|
||||
const licenseService = new LicenseService();
|
||||
const pw = new PolicyWatcher(
|
||||
packagePolicySvcMock,
|
||||
soStartMock,
|
||||
esStartMock,
|
||||
cloudServiceMock,
|
||||
logger
|
||||
);
|
||||
const pw = new PolicyWatcher(packagePolicySvcMock, soStartMock, esStartMock, logger);
|
||||
|
||||
// swap out watch function, just to ensure it gets called when a license change happens
|
||||
const mockWatch = jest.fn();
|
||||
|
@ -104,13 +96,7 @@ describe('Policy-Changing license watcher', () => {
|
|||
perPage: 100,
|
||||
});
|
||||
|
||||
const pw = new PolicyWatcher(
|
||||
packagePolicySvcMock,
|
||||
soStartMock,
|
||||
esStartMock,
|
||||
cloudServiceMock,
|
||||
logger
|
||||
);
|
||||
const pw = new PolicyWatcher(packagePolicySvcMock, soStartMock, esStartMock, logger);
|
||||
await pw.watch(Gold); // just manually trigger with a given license
|
||||
|
||||
expect(packagePolicySvcMock.list.mock.calls.length).toBe(3); // should have asked for 3 pages of resuts
|
||||
|
@ -137,13 +123,7 @@ describe('Policy-Changing license watcher', () => {
|
|||
perPage: 100,
|
||||
});
|
||||
|
||||
const pw = new PolicyWatcher(
|
||||
packagePolicySvcMock,
|
||||
soStartMock,
|
||||
esStartMock,
|
||||
cloudServiceMock,
|
||||
logger
|
||||
);
|
||||
const pw = new PolicyWatcher(packagePolicySvcMock, soStartMock, esStartMock, logger);
|
||||
|
||||
// emulate a license change below paid tier
|
||||
await pw.watch(Basic);
|
||||
|
|
|
@ -17,7 +17,6 @@ import type {
|
|||
} from '@kbn/core/server';
|
||||
import type { PackagePolicy } from '@kbn/fleet-plugin/common';
|
||||
import { PACKAGE_POLICY_SAVED_OBJECT_TYPE } from '@kbn/fleet-plugin/common';
|
||||
import type { CloudSetup } from '@kbn/cloud-plugin/server';
|
||||
import type { PackagePolicyClient } from '@kbn/fleet-plugin/server';
|
||||
import type { ILicense } from '@kbn/licensing-plugin/common/types';
|
||||
import { SECURITY_EXTENSION_ID } from '@kbn/core-saved-objects-server';
|
||||
|
@ -35,19 +34,16 @@ export class PolicyWatcher {
|
|||
private policyService: PackagePolicyClient;
|
||||
private subscription: Subscription | undefined;
|
||||
private soStart: SavedObjectsServiceStart;
|
||||
private cloud: CloudSetup;
|
||||
constructor(
|
||||
policyService: PackagePolicyClient,
|
||||
soStart: SavedObjectsServiceStart,
|
||||
esStart: ElasticsearchServiceStart,
|
||||
cloud: CloudSetup,
|
||||
logger: Logger
|
||||
) {
|
||||
this.policyService = policyService;
|
||||
this.esClient = esStart.client.asInternalUser;
|
||||
this.logger = logger;
|
||||
this.soStart = soStart;
|
||||
this.cloud = cloud;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -105,9 +101,6 @@ export class PolicyWatcher {
|
|||
for (const policy of response.items as PolicyData[]) {
|
||||
const updatePolicy = getPolicyDataForUpdate(policy);
|
||||
const policyConfig = updatePolicy.inputs[0].config.policy.value;
|
||||
updatePolicy.inputs[0].config.policy.value.meta.license = license.type || '';
|
||||
// add cloud info to policy meta
|
||||
updatePolicy.inputs[0].config.policy.value.meta.cloud = this.cloud?.isCloudEnabled;
|
||||
|
||||
try {
|
||||
if (!isEndpointPolicyValidForLicense(policyConfig, license)) {
|
||||
|
|
|
@ -366,7 +366,8 @@ describe('ingest_integration tests ', () => {
|
|||
logger,
|
||||
licenseService,
|
||||
endpointAppContextMock.featureUsageService,
|
||||
endpointAppContextMock.endpointMetadataService
|
||||
endpointAppContextMock.endpointMetadataService,
|
||||
cloudService
|
||||
);
|
||||
const policyConfig = generator.generatePolicyPackagePolicy();
|
||||
policyConfig.inputs[0]!.config!.policy.value = mockPolicy;
|
||||
|
@ -382,7 +383,8 @@ describe('ingest_integration tests ', () => {
|
|||
logger,
|
||||
licenseService,
|
||||
endpointAppContextMock.featureUsageService,
|
||||
endpointAppContextMock.endpointMetadataService
|
||||
endpointAppContextMock.endpointMetadataService,
|
||||
cloudService
|
||||
);
|
||||
const policyConfig = generator.generatePolicyPackagePolicy();
|
||||
policyConfig.inputs[0]!.config!.policy.value = mockPolicy;
|
||||
|
@ -412,7 +414,8 @@ describe('ingest_integration tests ', () => {
|
|||
logger,
|
||||
licenseService,
|
||||
endpointAppContextMock.featureUsageService,
|
||||
endpointAppContextMock.endpointMetadataService
|
||||
endpointAppContextMock.endpointMetadataService,
|
||||
cloudService
|
||||
);
|
||||
const policyConfig = generator.generatePolicyPackagePolicy();
|
||||
policyConfig.inputs[0]!.config!.policy.value = mockPolicy;
|
||||
|
@ -427,6 +430,66 @@ describe('ingest_integration tests ', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('package policy update callback when meta fields should be updated', () => {
|
||||
const soClient = savedObjectsClientMock.create();
|
||||
const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser;
|
||||
|
||||
beforeEach(() => {
|
||||
licenseEmitter.next(Platinum); // set license level to platinum
|
||||
});
|
||||
it('updates successfully when meta fields differ from services', async () => {
|
||||
const mockPolicy = policyFactory();
|
||||
mockPolicy.meta.cloud = true; // cloud mock will return true
|
||||
mockPolicy.meta.license = 'platinum'; // license is set to emit platinum
|
||||
const logger = loggingSystemMock.create().get('ingest_integration.test');
|
||||
const callback = getPackagePolicyUpdateCallback(
|
||||
logger,
|
||||
licenseService,
|
||||
endpointAppContextMock.featureUsageService,
|
||||
endpointAppContextMock.endpointMetadataService,
|
||||
cloudService
|
||||
);
|
||||
const policyConfig = generator.generatePolicyPackagePolicy();
|
||||
// values should be updated
|
||||
policyConfig.inputs[0]!.config!.policy.value.meta.cloud = false;
|
||||
policyConfig.inputs[0]!.config!.policy.value.meta.license = 'gold';
|
||||
const updatedPolicyConfig = await callback(
|
||||
policyConfig,
|
||||
soClient,
|
||||
esClient,
|
||||
requestContextMock.convertContext(ctx),
|
||||
req
|
||||
);
|
||||
expect(updatedPolicyConfig.inputs[0]!.config!.policy.value).toEqual(mockPolicy);
|
||||
});
|
||||
|
||||
it('meta fields stay the same where there is no difference', async () => {
|
||||
const mockPolicy = policyFactory();
|
||||
mockPolicy.meta.cloud = true; // cloud mock will return true
|
||||
mockPolicy.meta.license = 'platinum'; // license is set to emit platinum
|
||||
const logger = loggingSystemMock.create().get('ingest_integration.test');
|
||||
const callback = getPackagePolicyUpdateCallback(
|
||||
logger,
|
||||
licenseService,
|
||||
endpointAppContextMock.featureUsageService,
|
||||
endpointAppContextMock.endpointMetadataService,
|
||||
cloudService
|
||||
);
|
||||
const policyConfig = generator.generatePolicyPackagePolicy();
|
||||
// values should be updated
|
||||
policyConfig.inputs[0]!.config!.policy.value.meta.cloud = true;
|
||||
policyConfig.inputs[0]!.config!.policy.value.meta.license = 'platinum';
|
||||
const updatedPolicyConfig = await callback(
|
||||
policyConfig,
|
||||
soClient,
|
||||
esClient,
|
||||
requestContextMock.convertContext(ctx),
|
||||
req
|
||||
);
|
||||
expect(updatedPolicyConfig.inputs[0]!.config!.policy.value).toEqual(mockPolicy);
|
||||
});
|
||||
});
|
||||
|
||||
describe('package policy delete callback', () => {
|
||||
const soClient = savedObjectsClientMock.create();
|
||||
const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser;
|
||||
|
|
|
@ -44,6 +44,17 @@ const isEndpointPackagePolicy = <T extends { package?: { name: string } }>(
|
|||
return packagePolicy.package?.name === 'endpoint';
|
||||
};
|
||||
|
||||
const shouldUpdateMetaValues = (
|
||||
endpointPackagePolicy: PolicyConfig,
|
||||
currentLicenseType: string,
|
||||
currentCloudInfo: boolean
|
||||
) => {
|
||||
return (
|
||||
endpointPackagePolicy.meta.license !== currentLicenseType ||
|
||||
endpointPackagePolicy.meta.cloud !== currentCloudInfo
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Callback to handle creation of PackagePolicies in Fleet
|
||||
*/
|
||||
|
@ -152,7 +163,8 @@ export const getPackagePolicyUpdateCallback = (
|
|||
logger: Logger,
|
||||
licenseService: LicenseService,
|
||||
featureUsageService: FeatureUsageService,
|
||||
endpointMetadataService: EndpointMetadataService
|
||||
endpointMetadataService: EndpointMetadataService,
|
||||
cloud: CloudSetup
|
||||
): PutPackagePolicyUpdateCallback => {
|
||||
return async (newPackagePolicy: NewPackagePolicy): Promise<UpdatePackagePolicy> => {
|
||||
if (!isEndpointPackagePolicy(newPackagePolicy)) {
|
||||
|
@ -170,6 +182,22 @@ export const getPackagePolicyUpdateCallback = (
|
|||
|
||||
notifyProtectionFeatureUsage(newPackagePolicy, featureUsageService, endpointMetadataService);
|
||||
|
||||
const newEndpointPackagePolicy = newPackagePolicy.inputs[0].config?.policy
|
||||
?.value as PolicyConfig;
|
||||
|
||||
if (
|
||||
newPackagePolicy.inputs[0].config?.policy?.value &&
|
||||
shouldUpdateMetaValues(
|
||||
newEndpointPackagePolicy,
|
||||
licenseService.getLicenseType(),
|
||||
cloud?.isCloudEnabled
|
||||
)
|
||||
) {
|
||||
newEndpointPackagePolicy.meta.license = licenseService.getLicenseType();
|
||||
newEndpointPackagePolicy.meta.cloud = cloud?.isCloudEnabled;
|
||||
newPackagePolicy.inputs[0].config.policy.value = newEndpointPackagePolicy;
|
||||
}
|
||||
|
||||
return newPackagePolicy;
|
||||
};
|
||||
};
|
||||
|
|
|
@ -472,7 +472,6 @@ export class Plugin implements ISecuritySolutionPlugin {
|
|||
plugins.fleet.packagePolicyService,
|
||||
core.savedObjects,
|
||||
core.elasticsearch,
|
||||
plugins.cloud,
|
||||
logger
|
||||
);
|
||||
this.policyWatcher.start(licenseService);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue