Revert "[Infra][ECO] Fix RBAC issue in hosts view" (#202418)

Reverts elastic/kibana#199841
This commit is contained in:
Cauê Marcondes 2024-12-02 12:03:24 +00:00 committed by GitHub
parent a18be1fb17
commit 99463c7a0d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
19 changed files with 158 additions and 49 deletions

View file

@ -72,7 +72,10 @@ export function registerAssistantFunctions({
ruleDataClient,
plugins,
getApmIndices: async () => {
const apmIndices = await plugins.apmDataAccess.setup.getApmIndices();
const coreContext = await resources.context.core;
const apmIndices = await plugins.apmDataAccess.setup.getApmIndices(
coreContext.savedObjects.client
);
return apmIndices;
},
};

View file

@ -16,6 +16,7 @@ import { registerAssistantFunctions } from './assistant_functions';
import { registerDeprecations } from './deprecations';
import { APM_FEATURE, registerFeaturesUsage } from './feature';
import { createApmTelemetry } from './lib/apm_telemetry';
import { getInternalSavedObjectsClient } from './lib/helpers/get_internal_saved_objects_client';
import {
APM_RULE_TYPE_ALERT_CONTEXT,
apmRuleTypeAlertFieldMap,
@ -114,6 +115,13 @@ export class APMPlugin
};
}) as APMRouteHandlerResources['plugins'];
const apmIndicesPromise = (async () => {
const coreStart = await getCoreStart();
const soClient = await getInternalSavedObjectsClient(coreStart);
const { getApmIndices } = plugins.apmDataAccess;
return getApmIndices(soClient);
})();
// This if else block will go away in favour of removing Home Tutorial Integration
// Ideally we will directly register a custom integration and pass the configs
// for cloud, onPrem and Serverless so that the actual component can take
@ -121,8 +129,7 @@ export class APMPlugin
if (currentConfig.serverlessOnboarding && plugins.customIntegrations) {
plugins.customIntegrations?.registerCustomIntegration(apmTutorialCustomIntegration);
} else {
plugins.apmDataAccess
.getApmIndices()
apmIndicesPromise
.then((apmIndices) => {
plugins.home?.tutorials.registerTutorial(
tutorialProvider({

View file

@ -115,7 +115,10 @@ export function registerRoutes({
);
const getApmIndices = async () => {
const apmIndices = await plugins.apmDataAccess.setup.getApmIndices();
const coreContext = await context.core;
const apmIndices = await plugins.apmDataAccess.setup.getApmIndices(
coreContext.savedObjects.client
);
return apmIndices;
};

View file

@ -38,7 +38,8 @@ export const getAlertDetailsContextHandler = (
return async (requestContext, query) => {
const resources = {
getApmIndices: async () => {
return resourcePlugins.apmDataAccess.setup.getApmIndices();
const coreContext = await requestContext.core;
return resourcePlugins.apmDataAccess.setup.getApmIndices(coreContext.savedObjects.client);
},
request: requestContext.request,
params: { query: { _inspect: false } },

View file

@ -5,7 +5,7 @@
* 2.0.
*/
import { Logger, CoreStart } from '@kbn/core/server';
import { Logger, CoreStart, SavedObjectsClientContract } from '@kbn/core/server';
import {
FleetStartContract,
PostPackagePolicyCreateCallback,
@ -22,6 +22,7 @@ import {
SOURCE_MAP_API_KEY_PATH,
} from './get_package_policy_decorators';
import { createInternalESClient } from '../../lib/helpers/create_es_client/create_internal_es_client';
import { getInternalSavedObjectsClient } from '../../lib/helpers/get_internal_saved_objects_client';
import { APMRouteHandlerResources } from '../apm_routes/register_apm_server_routes';
export async function registerFleetPolicyCallbacks({
@ -148,7 +149,7 @@ function onPackagePolicyCreateOrUpdate({
coreStart,
}: {
fleetPluginStart: FleetStartContract;
getApmIndices: () => Promise<APMIndices>;
getApmIndices: (soClient: SavedObjectsClientContract) => Promise<APMIndices>;
coreStart: CoreStart;
}): PutPackagePolicyUpdateCallback & PostPackagePolicyCreateCallback {
return async (packagePolicy) => {
@ -157,7 +158,8 @@ function onPackagePolicyCreateOrUpdate({
}
const { asInternalUser } = coreStart.elasticsearch.client;
const apmIndices = await getApmIndices();
const savedObjectsClient = await getInternalSavedObjectsClient(coreStart);
const apmIndices = await getApmIndices(savedObjectsClient);
const internalESClient = await createInternalESClient({
debug: false,

View file

@ -15,7 +15,9 @@
"requiredPlugins": [
"data"
],
"optionalPlugins": [],
"optionalPlugins": [
"security"
],
"requiredBundles": []
}
}

View file

@ -91,6 +91,7 @@ export type {
APMEventESSearchRequest,
APMLogEventESSearchRequest,
DocumentSourcesRequest,
ApmDataAccessPrivilegesCheck,
HostNamesRequest,
GetDocumentTypeParams,
} from './types';

View file

@ -0,0 +1,42 @@
/*
* 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 { KibanaRequest } from '@kbn/core-http-server';
import { SecurityPluginStart } from '@kbn/security-plugin-types-server';
import { mapValues } from 'lodash';
import { APMIndices } from '..';
export interface ApmDataAccessPrivilegesCheck {
request: KibanaRequest;
security?: SecurityPluginStart;
getApmIndices: () => Promise<APMIndices>;
}
export async function checkPrivileges({
request,
getApmIndices,
security,
}: ApmDataAccessPrivilegesCheck) {
const authorization = security?.authz;
if (!authorization) {
return true;
}
const [apmIndices, checkPrivilegesFn] = await Promise.all([
getApmIndices(),
authorization.checkPrivilegesDynamicallyWithRequest(request),
]);
const { hasAllRequested } = await checkPrivilegesFn({
elasticsearch: {
cluster: [],
index: mapValues(apmIndices, () => ['read']),
},
});
return hasAllRequested;
}

View file

@ -5,19 +5,32 @@
* 2.0.
*/
import { PluginInitializerContext, CoreSetup, CoreStart, Plugin, Logger } from '@kbn/core/server';
import {
PluginInitializerContext,
CoreSetup,
CoreStart,
Plugin,
SavedObjectsClientContract,
Logger,
} from '@kbn/core/server';
import { APMDataAccessConfig } from '.';
import { ApmDataAccessPluginSetup, ApmDataAccessPluginStart } from './types';
import {
ApmDataAccessPluginSetup,
ApmDataAccessPluginStart,
ApmDataAccessServerDependencies,
} from './types';
import { migrateLegacyAPMIndicesToSpaceAware } from './saved_objects/migrations/migrate_legacy_apm_indices_to_space_aware';
import {
apmIndicesSavedObjectDefinition,
getApmIndicesSavedObject,
} from './saved_objects/apm_indices';
import { getServices } from './services/get_services';
import { ApmDataAccessPrivilegesCheck, checkPrivileges } from './lib/check_privileges';
export class ApmDataAccessPlugin
implements Plugin<ApmDataAccessPluginSetup, ApmDataAccessPluginStart>
{
public server?: ApmDataAccessServerDependencies;
public config: APMDataAccessConfig;
public logger: Logger;
@ -26,34 +39,45 @@ export class ApmDataAccessPlugin
this.logger = initContext.logger.get();
}
getApmIndices = async (savedObjectsClient: SavedObjectsClientContract) => {
const apmIndicesFromSavedObject = await getApmIndicesSavedObject(savedObjectsClient);
return { ...this.config.indices, ...apmIndicesFromSavedObject };
};
public setup(core: CoreSetup): ApmDataAccessPluginSetup {
// register saved object
core.savedObjects.registerType(apmIndicesSavedObjectDefinition);
const getApmIndices = async () => {
const [coreStart] = await core.getStartServices();
const soClient = await coreStart.savedObjects.createInternalRepository();
const apmIndicesFromSavedObject = await getApmIndicesSavedObject(soClient);
return { ...this.config.indices, ...apmIndicesFromSavedObject };
};
// expose
return {
apmIndicesFromConfigFile: this.config.indices,
getApmIndices,
getApmIndices: this.getApmIndices,
getServices,
};
}
public start(core: CoreStart) {
public start(core: CoreStart, plugins: ApmDataAccessServerDependencies) {
// TODO: remove in 9.0
migrateLegacyAPMIndicesToSpaceAware({ coreStart: core, logger: this.logger }).catch((e) => {
this.logger.error('Failed to run migration making APM indices space aware');
this.logger.error(e);
});
return {};
const getApmIndicesWithInternalUserFn = async () => {
const soClient = core.savedObjects.createInternalRepository();
return this.getApmIndices(soClient);
};
const startServices = {
hasPrivileges: ({ request }: Pick<ApmDataAccessPrivilegesCheck, 'request'>) =>
checkPrivileges({
request,
getApmIndices: getApmIndicesWithInternalUserFn,
security: plugins.security,
}),
};
return { ...startServices };
}
public stop() {}

View file

@ -5,17 +5,28 @@
* 2.0.
*/
import { SavedObjectsClientContract } from '@kbn/core-saved-objects-api-server';
import type { SecurityPluginStart } from '@kbn/security-plugin-types-server';
import type { APMIndices } from '.';
import { getServices } from './services/get_services';
import type { ApmDataAccessPrivilegesCheck } from './lib/check_privileges';
export interface ApmDataAccessPluginSetup {
apmIndicesFromConfigFile: APMIndices;
getApmIndices: () => Promise<APMIndices>;
getApmIndices: (soClient: SavedObjectsClientContract) => Promise<APMIndices>;
getServices: typeof getServices;
}
// eslint-disable-next-line @typescript-eslint/no-empty-interface
export interface ApmDataAccessPluginStart {}
export interface ApmDataAccessServerDependencies {
security?: SecurityPluginStart;
}
export interface ApmDataAccessPluginStart {
hasPrivileges: (params: Pick<ApmDataAccessPrivilegesCheck, 'request'>) => Promise<boolean>;
}
export interface ApmDataAccessServerDependencies {
security?: SecurityPluginStart;
}
export type ApmDataAccessServices = ReturnType<typeof getServices>;
export type { ApmDataAccessServicesParams } from './services/get_services';
@ -27,3 +38,4 @@ export type {
APMEventESSearchRequest,
APMLogEventESSearchRequest,
} from './lib/helpers';
export type { ApmDataAccessPrivilegesCheck };

View file

@ -9,6 +9,7 @@
"@kbn/config-schema",
"@kbn/core",
"@kbn/i18n",
"@kbn/core-saved-objects-api-server",
"@kbn/data-plugin",
"@kbn/inspector-plugin",
"@kbn/observability-plugin",
@ -17,6 +18,8 @@
"@kbn/apm-types",
"@kbn/core-http-server-mocks",
"@kbn/apm-utils",
"@kbn/core-http-server",
"@kbn/security-plugin-types-server",
"@kbn/utility-types",
"@kbn/elastic-agent-utils",
"@kbn/observability-utils-common"

View file

@ -27,17 +27,23 @@ export const getApmDataAccessClient = ({
context: InfraPluginRequestHandlerContext;
request: KibanaRequest;
}) => {
const hasPrivileges = async () => {
const apmDataAccessStart = await libs.plugins.apmDataAccess.start();
return apmDataAccessStart.hasPrivileges({ request });
};
const getServices = async () => {
const apmDataAccess = libs.plugins.apmDataAccess.setup;
const coreContext = await context.core;
const { uiSettings, elasticsearch } = coreContext;
const { savedObjects, uiSettings, elasticsearch } = coreContext;
const savedObjectsClient = savedObjects.client;
const esClient = elasticsearch.client.asCurrentUser;
const uiSettingsClient = uiSettings.client;
const [apmIndices, includeFrozen] = await Promise.all([
apmDataAccess.getApmIndices(),
apmDataAccess.getApmIndices(savedObjectsClient),
uiSettingsClient.get<boolean>(UI_SETTINGS.SEARCH_INCLUDE_FROZEN),
]);
@ -80,5 +86,5 @@ export const getApmDataAccessClient = ({
};
};
return { getServices };
return { hasPrivileges, getServices };
};

View file

@ -41,11 +41,12 @@ export const initInfraAssetRoutes = (libs: InfraBackendLibs) => {
try {
const apmDataAccessClient = getApmDataAccessClient({ request, libs, context });
const hasApmPrivileges = await apmDataAccessClient.hasPrivileges();
const [infraMetricsClient, alertsClient, apmDataAccessServices] = await Promise.all([
getInfraMetricsClient({ request, libs, context }),
getInfraAlertsClient({ libs, request }),
apmDataAccessClient.getServices(),
hasApmPrivileges ? apmDataAccessClient.getServices() : undefined,
]);
const hosts = await getHosts({
@ -96,10 +97,11 @@ export const initInfraAssetRoutes = (libs: InfraBackendLibs) => {
try {
const apmDataAccessClient = getApmDataAccessClient({ request, libs, context });
const hasApmPrivileges = await apmDataAccessClient.hasPrivileges();
const [infraMetricsClient, apmDataAccessServices] = await Promise.all([
getInfraMetricsClient({ request, libs, context }),
apmDataAccessClient.getServices(),
hasApmPrivileges ? apmDataAccessClient.getServices() : undefined,
]);
const count = await getHostsCount({

View file

@ -9,7 +9,6 @@ import { findInventoryModel } from '@kbn/metrics-data-access-plugin/common';
import { termQuery } from '@kbn/observability-plugin/server';
import { ApmDocumentType, type TimeRangeMetadata } from '@kbn/apm-data-access-plugin/common';
import { estypes } from '@elastic/elasticsearch';
import { castArray } from 'lodash';
import type { ApmDataAccessServicesWrapper } from '../../../../lib/helpers/get_apm_data_access_client';
import {
EVENT_MODULE,
@ -18,16 +17,12 @@ import {
} from '../../../../../common/constants';
import type { InfraAssetMetricType } from '../../../../../common/http_api/infra';
export const getFilterByIntegration = (
integration: typeof SYSTEM_INTEGRATION,
extraFilter: estypes.QueryDslQueryContainer[] = []
) => {
export const getFilterByIntegration = (integration: typeof SYSTEM_INTEGRATION) => {
return {
bool: {
should: [
...termQuery(EVENT_MODULE, integration),
...termQuery(METRICSET_MODULE, integration),
...extraFilter,
],
minimum_should_match: 1,
},
@ -68,6 +63,7 @@ export const getDocumentsFilter = async ({
from: number;
to: number;
}) => {
const filters: estypes.QueryDslQueryContainer[] = [getFilterByIntegration('system')];
const apmDocumentsFilter =
apmDataAccessServices && apmDocumentSources
? await getApmDocumentsFilter({
@ -78,9 +74,9 @@ export const getDocumentsFilter = async ({
})
: undefined;
const filters: estypes.QueryDslQueryContainer[] = [
getFilterByIntegration('system', apmDocumentsFilter && castArray(apmDocumentsFilter)),
];
if (apmDocumentsFilter) {
filters.push(apmDocumentsFilter);
}
return filters;
};

View file

@ -49,7 +49,6 @@ export const getHosts = async ({
const [hostMetricsResponse, alertsCountResponse] = await Promise.all([
getAllHosts({
infraMetricsClient,
apmDataAccessServices,
apmDocumentSources,
from,
to,

View file

@ -25,14 +25,8 @@ export async function getHostsCount({
}) {
assertQueryStructure(query);
const apmDocumentSources = await apmDataAccessServices?.getDocumentSources({
start: from,
end: to,
});
const documentsFilter = await getDocumentsFilter({
apmDataAccessServices,
apmDocumentSources,
from,
to,
});
@ -45,7 +39,7 @@ export async function getHostsCount({
query: {
bool: {
filter: [query, ...rangeQuery(from, to)],
must: [...documentsFilter],
should: [...documentsFilter],
},
},
aggs: {

View file

@ -13,5 +13,5 @@ import { InfraMetricsClient } from '../../../lib/helpers/get_infra_metrics_clien
export interface GetHostParameters extends GetInfraMetricsRequestBodyPayload {
infraMetricsClient: InfraMetricsClient;
alertsClient: InfraAlertsClient;
apmDataAccessServices: ApmDataAccessServicesWrapper;
apmDataAccessServices?: ApmDataAccessServicesWrapper;
}

View file

@ -36,6 +36,16 @@ export const initServicesRoute = (libs: InfraBackendLibs) => {
const { from, to, size = 10, validatedFilters } = request.query;
const apmDataAccessClient = getApmDataAccessClient({ request, libs, context });
const hasApmPrivileges = await apmDataAccessClient.hasPrivileges();
if (!hasApmPrivileges) {
return response.customError({
statusCode: 403,
body: {
message: 'APM data access service is not available',
},
});
}
const apmDataAccessServices = await apmDataAccessClient.getServices();

View file

@ -52,7 +52,9 @@ export function registerTopNFunctionsAPMTransactionsRoute({
});
}
const core = await context.core;
const { transaction: transactionIndices } = await apmDataAccess.getApmIndices();
const { transaction: transactionIndices } = await apmDataAccess.getApmIndices(
core.savedObjects.client
);
const esClient = await getClient(context);