diff --git a/config/opensearch_dashboards.yml b/config/opensearch_dashboards.yml index 9c6c040433b4..2fb4496c091e 100644 --- a/config/opensearch_dashboards.yml +++ b/config/opensearch_dashboards.yml @@ -29,6 +29,10 @@ # dashboards. OpenSearch Dashboards creates a new index if the index doesn't already exist. #opensearchDashboards.index: ".opensearch_dashboards" +# OpenSearch Dashboards uses an index in OpenSearch to store dynamic configurations. +# This shall be a different index from opensearchDashboards.index. +# opensearchDashboards.dynamic_config_index: ".opensearch_dashboards_config" + # The default application to load. #opensearchDashboards.defaultAppId: "home" diff --git a/src/core/server/opensearch_dashboards_config.ts b/src/core/server/opensearch_dashboards_config.ts index 107d02ea3377..a4b18f88dc77 100644 --- a/src/core/server/opensearch_dashboards_config.ts +++ b/src/core/server/opensearch_dashboards_config.ts @@ -48,6 +48,7 @@ export const config = { schema: schema.object({ enabled: schema.boolean({ defaultValue: true }), index: schema.string({ defaultValue: '.kibana' }), + dynamic_config_index: schema.string({ defaultValue: '.opensearch_dashboards_config' }), autocompleteTerminateAfter: schema.duration({ defaultValue: 100000 }), autocompleteTimeout: schema.duration({ defaultValue: 1000 }), branding: schema.object({ diff --git a/src/core/server/plugins/plugin_context.test.ts b/src/core/server/plugins/plugin_context.test.ts index 48c9eb6d6823..f3a6d07e5780 100644 --- a/src/core/server/plugins/plugin_context.test.ts +++ b/src/core/server/plugins/plugin_context.test.ts @@ -98,6 +98,7 @@ describe('createPluginInitializerContext', () => { expect(configObject).toStrictEqual({ opensearchDashboards: { index: '.kibana', + dynamic_config_index: '.opensearch_dashboards_config', autocompleteTerminateAfter: duration(100000), autocompleteTimeout: duration(1000), }, diff --git a/src/core/server/plugins/types.ts b/src/core/server/plugins/types.ts index b7667b5bd2d2..345d5e618ac3 100644 --- a/src/core/server/plugins/types.ts +++ b/src/core/server/plugins/types.ts @@ -287,7 +287,12 @@ export interface Plugin< export const SharedGlobalConfigKeys = { // We can add more if really needed - opensearchDashboards: ['index', 'autocompleteTerminateAfter', 'autocompleteTimeout'] as const, + opensearchDashboards: [ + 'index', + 'dynamic_config_index', + 'autocompleteTerminateAfter', + 'autocompleteTimeout', + ] as const, opensearch: ['shardTimeout', 'requestTimeout', 'pingTimeout'] as const, path: ['data'] as const, savedObjects: ['maxImportPayloadBytes'] as const, diff --git a/src/legacy/server/config/schema.js b/src/legacy/server/config/schema.js index 5cf8e9ac1901..ce324fda177d 100644 --- a/src/legacy/server/config/schema.js +++ b/src/legacy/server/config/schema.js @@ -227,6 +227,7 @@ export default () => opensearchDashboards: Joi.object({ enabled: Joi.boolean().default(true), index: Joi.string().default('.kibana'), + dynamic_config_index: Joi.string().default('.opensearch_dashboards_config'), autocompleteTerminateAfter: Joi.number().integer().min(1).default(100000), // TODO Also allow units here like in opensearch config once this is moved to the new platform autocompleteTimeout: Joi.number().integer().min(1).default(1000), diff --git a/src/plugins/csp_configuration_provider/server/csp_handlers.test.ts b/src/plugins/csp_configuration_provider/server/csp_handlers.test.ts index 7dcaa0abda3c..6feb35574842 100644 --- a/src/plugins/csp_configuration_provider/server/csp_handlers.test.ts +++ b/src/plugins/csp_configuration_provider/server/csp_handlers.test.ts @@ -30,6 +30,8 @@ const forgeRequest = ({ }); }; +const DEFAULT_DYNAMIC_CONFIG_INDEX = '.opensearch_dashboards_config'; + describe('CSP handlers', () => { let toolkit: ReturnType; @@ -49,7 +51,11 @@ describe('CSP handlers', () => { const getCspClient = jest.fn().mockReturnValue(cspClient); - const handler = createCspRulesPreResponseHandler(coreSetup, getCspClient); + const handler = createCspRulesPreResponseHandler( + coreSetup, + DEFAULT_DYNAMIC_CONFIG_INDEX, + getCspClient + ); const request = forgeRequest({ method: 'get', headers: { 'sec-fetch-dest': 'document' } }); toolkit.next.mockReturnValue('next' as any); @@ -81,7 +87,11 @@ describe('CSP handlers', () => { const getCspClient = jest.fn().mockReturnValue(cspClient); - const handler = createCspRulesPreResponseHandler(coreSetup, getCspClient); + const handler = createCspRulesPreResponseHandler( + coreSetup, + DEFAULT_DYNAMIC_CONFIG_INDEX, + getCspClient + ); const request = forgeRequest({ method: 'get', headers: { 'sec-fetch-dest': 'document' } }); toolkit.next.mockReturnValue('next' as any); @@ -107,7 +117,11 @@ describe('CSP handlers', () => { const getCspClient = jest.fn().mockReturnValue(cspClient); - const handler = createCspRulesPreResponseHandler(coreSetup, getCspClient); + const handler = createCspRulesPreResponseHandler( + coreSetup, + DEFAULT_DYNAMIC_CONFIG_INDEX, + getCspClient + ); const request = forgeRequest({ method: 'get', headers: { 'sec-fetch-dest': 'document' } }); toolkit.next.mockReturnValue('next' as any); @@ -133,7 +147,11 @@ describe('CSP handlers', () => { const getCspClient = jest.fn().mockReturnValue(cspClient); - const handler = createCspRulesPreResponseHandler(coreSetup, getCspClient); + const handler = createCspRulesPreResponseHandler( + coreSetup, + DEFAULT_DYNAMIC_CONFIG_INDEX, + getCspClient + ); const cssSecFetchDest = 'css'; const request = forgeRequest({ method: 'get', headers: { 'sec-fetch-dest': cssSecFetchDest } }); @@ -161,7 +179,11 @@ describe('CSP handlers', () => { const getCspClient = jest.fn().mockReturnValue(cspClient); - const handler = createCspRulesPreResponseHandler(coreSetup, getCspClient); + const handler = createCspRulesPreResponseHandler( + coreSetup, + DEFAULT_DYNAMIC_CONFIG_INDEX, + getCspClient + ); const request = forgeRequest({ method: 'get', headers: {} }); diff --git a/src/plugins/csp_configuration_provider/server/csp_handlers.ts b/src/plugins/csp_configuration_provider/server/csp_handlers.ts index 5a2ff8158e47..378a38523ec8 100644 --- a/src/plugins/csp_configuration_provider/server/csp_handlers.ts +++ b/src/plugins/csp_configuration_provider/server/csp_handlers.ts @@ -6,11 +6,11 @@ import { CoreSetup, OnPreResponseHandler, OpenSearchClient } from '../../../core/server'; import { CspClient } from './types'; -const OPENSEARCH_DASHBOARDS_CONFIG_INDEX_NAME = '.opensearch_dashboards_config'; const OPENSEARCH_DASHBOARDS_CONFIG_DOCUMENT_NAME = 'csp.rules'; export function createCspRulesPreResponseHandler( core: CoreSetup, + dynamicConfigIndex: string, getCspClient: (inputOpenSearchClient: OpenSearchClient) => CspClient ): OnPreResponseHandler { return async (request, response, toolkit) => { @@ -26,14 +26,14 @@ export function createCspRulesPreResponseHandler( const myClient = getCspClient(coreStart.opensearch.client.asInternalUser); - const existsValue = await myClient.exists(OPENSEARCH_DASHBOARDS_CONFIG_INDEX_NAME); + const existsValue = await myClient.exists(dynamicConfigIndex); if (!existsValue) { return toolkit.next({}); } const cspRules = await myClient.get( - OPENSEARCH_DASHBOARDS_CONFIG_INDEX_NAME, + dynamicConfigIndex, OPENSEARCH_DASHBOARDS_CONFIG_DOCUMENT_NAME ); diff --git a/src/plugins/csp_configuration_provider/server/plugin.ts b/src/plugins/csp_configuration_provider/server/plugin.ts index 3e5b390bece6..e483fb219a42 100644 --- a/src/plugins/csp_configuration_provider/server/plugin.ts +++ b/src/plugins/csp_configuration_provider/server/plugin.ts @@ -3,6 +3,8 @@ * SPDX-License-Identifier: Apache-2.0 */ +import { Observable } from 'rxjs'; +import { first } from 'rxjs/operators'; import { CoreSetup, CoreStart, @@ -10,6 +12,7 @@ import { OpenSearchClient, Plugin, PluginInitializerContext, + SharedGlobalConfig, } from '../../../core/server'; import { createCspRulesPreResponseHandler } from './csp_handlers'; @@ -24,10 +27,12 @@ import { export class CspConfigurationProviderPlugin implements Plugin { private readonly logger: Logger; + private readonly config$: Observable; private cspClient: CspClient | undefined; constructor(initializerContext: PluginInitializerContext) { this.logger = initializerContext.logger.get(); + this.config$ = initializerContext.config.legacy.globalConfig$; } private setCspClient(inputCspClient: CspClient) { @@ -44,15 +49,21 @@ export class CspConfigurationProviderPlugin return this.cspClient; } - public setup(core: CoreSetup) { + public async setup(core: CoreSetup) { this.logger.debug('CspConfigurationProvider: Setup'); const router = core.http.createRouter(); // Register server side APIs defineRoutes(router); + const config = await this.config$.pipe(first()).toPromise(); + core.http.registerOnPreResponse( - createCspRulesPreResponseHandler(core, this.getCspClient.bind(this)) + createCspRulesPreResponseHandler( + core, + config.opensearchDashboards.dynamic_config_index, + this.getCspClient.bind(this) + ) ); return {