From 0857686efd15e8bed4b5a0ce44480be18ad24b47 Mon Sep 17 00:00:00 2001 From: Craig Perkins Date: Sat, 16 Mar 2024 10:08:02 -0400 Subject: [PATCH 1/7] Add TLS configuration for multiple data sources Signed-off-by: Craig Perkins --- src/plugins/data_source/config.ts | 9 +++ .../server/client/client_config.ts | 61 +++++++++++++++++-- 2 files changed, 66 insertions(+), 4 deletions(-) diff --git a/src/plugins/data_source/config.ts b/src/plugins/data_source/config.ts index 50013537b127..30824b486257 100644 --- a/src/plugins/data_source/config.ts +++ b/src/plugins/data_source/config.ts @@ -31,6 +31,15 @@ export const configSchema = schema.object({ defaultValue: new Array(32).fill(0), }), }), + ssl: schema.object({ + verificationMode: schema.oneOf( + [schema.literal('none'), schema.literal('certificate'), schema.literal('full')], + { defaultValue: 'full' } + ), + certificateAuthorities: schema.maybe( + schema.oneOf([schema.string(), schema.arrayOf(schema.string(), { minSize: 1 })]) + ), + }), clientPool: schema.object({ size: schema.number({ defaultValue: 5 }), }), diff --git a/src/plugins/data_source/server/client/client_config.ts b/src/plugins/data_source/server/client/client_config.ts index 61ffde2be748..50a17122cdfe 100644 --- a/src/plugins/data_source/server/client/client_config.ts +++ b/src/plugins/data_source/server/client/client_config.ts @@ -4,6 +4,7 @@ */ import { ClientOptions } from '@opensearch-project/opensearch'; +import { readFileSync } from 'fs'; import { DataSourcePluginConfigType } from '../../config'; /** @@ -18,14 +19,66 @@ export function parseClientOptions( endpoint: string, registeredSchema: any[] ): ClientOptions { + const verificationMode = config.ssl.verificationMode; + const sslConfig: any = { + requestCert: true, + }; + switch (verificationMode) { + case 'none': + sslConfig.rejectUnauthorized = false; + break; + case 'certificate': + sslConfig.rejectUnauthorized = true; + + // by default, NodeJS is checking the server identify + sslConfig.checkServerIdentity = () => undefined; + break; + case 'full': + sslConfig.rejectUnauthorized = true; + break; + default: + throw new Error(`Unknown ssl verificationMode: ${verificationMode}`); + } + + const { certificateAuthorities } = readCertificateAuthorities(config); + + sslConfig.ca = certificateAuthorities || []; + const clientOptions: ClientOptions = { node: endpoint, - ssl: { - requestCert: true, - rejectUnauthorized: true, - }, + ssl: sslConfig, plugins: registeredSchema, }; return clientOptions; } + +const readCertificateAuthorities = (rawConfig: any) => { + let certificateAuthorities: string[] | undefined; + + const addCAs = (ca: string[] | undefined) => { + if (ca && ca.length) { + certificateAuthorities = [...(certificateAuthorities || []), ...ca]; + } + }; + + const ca = rawConfig.ssl.certificateAuthorities; + if (ca) { + const parsed: string[] = []; + const paths = Array.isArray(ca) ? ca : [ca]; + if (paths.length > 0) { + for (const path of paths) { + parsed.push(readFile(path)); + } + addCAs(parsed); + } + } + + return { + certificateAuthorities, + }; +}; + +const readFile = (file: string) => { + return readFileSync(file, 'utf8'); +}; From 261dd8eefdc9d72c9a11f2dec09106fbbe9ee18f Mon Sep 17 00:00:00 2001 From: Craig Perkins Date: Sat, 16 Mar 2024 18:42:01 -0400 Subject: [PATCH 2/7] Add to CHANGELOG and add examples commented out in opensearch_dashboards.yml Signed-off-by: Craig Perkins --- CHANGELOG.md | 1 + config/opensearch_dashboards.yml | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8bf3d7338fa7..7943c7fd6749 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -43,6 +43,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - [Multiple Datasource] Add component to show single selected data source in read only mode ([#6125](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/6125)) - [Workspace] Add workspace id in basePath ([#6060](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/6060)) - Add sidecar service ([#5920](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5920)) +- [Multiple Datasource] Add TLS configuration for multiple data sources ([#6171](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/6171)) ### 🐛 Bug Fixes diff --git a/config/opensearch_dashboards.yml b/config/opensearch_dashboards.yml index 99df1d808bab..b5025f3a4bd3 100644 --- a/config/opensearch_dashboards.yml +++ b/config/opensearch_dashboards.yml @@ -281,6 +281,13 @@ # 'ff00::/8', # ] +# Optional setting that enables you to specify a path to PEM files for the certificate +# authority for your connected datasources. +#data_source.ssl.certificateAuthorities: [ "/path/to/your/CA.pem" ] + +# To disregard the validity of SSL certificates for connected data sources, change this setting's value to 'none'. +#data_source.ssl.verificationMode: full + # Set enabled false to hide authentication method in OpenSearch Dashboards. # If this setting is commented then all 3 options will be available in OpenSearch Dashboards. # Default value will be considered to True. From 9e3b24dcd1682b39127b3980c1fa9f9febd83077 Mon Sep 17 00:00:00 2001 From: Craig Perkins Date: Mon, 18 Mar 2024 10:40:22 -0400 Subject: [PATCH 3/7] Add tests and replace instance of any Signed-off-by: Craig Perkins --- .../server/client/client_config.test.ts | 67 +++++++++++++++++-- .../server/client/client_config.ts | 51 ++++++++------ 2 files changed, 92 insertions(+), 26 deletions(-) diff --git a/src/plugins/data_source/server/client/client_config.test.ts b/src/plugins/data_source/server/client/client_config.test.ts index c6dfff3fe4c6..e81677b49ae8 100644 --- a/src/plugins/data_source/server/client/client_config.test.ts +++ b/src/plugins/data_source/server/client/client_config.test.ts @@ -5,17 +5,20 @@ import { DataSourcePluginConfigType } from '../../config'; import { parseClientOptions } from './client_config'; -const TEST_DATA_SOURCE_ENDPOINT = 'http://test.com/'; +jest.mock('fs'); +const mockReadFileSync: jest.Mock = jest.requireMock('fs').readFileSync; -const config = { - enabled: true, - clientPool: { - size: 5, - }, -} as DataSourcePluginConfigType; +const TEST_DATA_SOURCE_ENDPOINT = 'http://test.com/'; describe('parseClientOptions', () => { test('include the ssl client configs as defaults', () => { + const config = { + enabled: true, + clientPool: { + size: 5, + }, + } as DataSourcePluginConfigType; + expect(parseClientOptions(config, TEST_DATA_SOURCE_ENDPOINT)).toEqual( expect.objectContaining({ node: TEST_DATA_SOURCE_ENDPOINT, @@ -26,4 +29,54 @@ describe('parseClientOptions', () => { }) ); }); + + test('test ssl config with verification mode set to none', () => { + const config = { + enabled: true, + ssl: { + verificationMode: 'none', + }, + clientPool: { + size: 5, + }, + } as DataSourcePluginConfigType; + expect(parseClientOptions(config, TEST_DATA_SOURCE_ENDPOINT)).toEqual( + expect.objectContaining({ + node: TEST_DATA_SOURCE_ENDPOINT, + ssl: { + requestCert: true, + rejectUnauthorized: false, + ca: [], + }, + }) + ); + }); + + test('test ssl config with verification mode set to full', () => { + const config = { + enabled: true, + ssl: { + verificationMode: 'full', + certificateAuthorities: ['some-path'], + }, + clientPool: { + size: 5, + }, + } as DataSourcePluginConfigType; + mockReadFileSync.mockReset(); + mockReadFileSync.mockImplementation((path: string) => `content-of-${path}`); + const parsedConfig = parseClientOptions(config, TEST_DATA_SOURCE_ENDPOINT); + expect(mockReadFileSync).toHaveBeenCalledTimes(1); + mockReadFileSync.mockClear(); + expect(parsedConfig).toEqual( + expect.objectContaining({ + node: TEST_DATA_SOURCE_ENDPOINT, + ssl: { + requestCert: true, + rejectUnauthorized: true, + ca: ['content-of-some-path'], + }, + }) + ); + }); }); diff --git a/src/plugins/data_source/server/client/client_config.ts b/src/plugins/data_source/server/client/client_config.ts index 50a17122cdfe..174f8371d2dc 100644 --- a/src/plugins/data_source/server/client/client_config.ts +++ b/src/plugins/data_source/server/client/client_config.ts @@ -5,8 +5,17 @@ import { ClientOptions } from '@opensearch-project/opensearch'; import { readFileSync } from 'fs'; +import { checkServerIdentity } from 'tls'; import { DataSourcePluginConfigType } from '../../config'; +/** @internal */ +type DataSourceSSLConfigOptions = Partial<{ + requestCert: boolean; + rejectUnauthorized: boolean; + checkServerIdentity: typeof checkServerIdentity; + ca: string[]; +}>; + /** * Parse the client options from given data source config and endpoint * @@ -19,30 +28,34 @@ export function parseClientOptions( endpoint: string, registeredSchema: any[] ): ClientOptions { - const verificationMode = config.ssl.verificationMode; - const sslConfig: any = { + const sslConfig: DataSourceSSLConfigOptions = { requestCert: true, + rejectUnauthorized: true, }; - switch (verificationMode) { - case 'none': - sslConfig.rejectUnauthorized = false; - break; - case 'certificate': - sslConfig.rejectUnauthorized = true; - // by default, NodeJS is checking the server identify - sslConfig.checkServerIdentity = () => undefined; - break; - case 'full': - sslConfig.rejectUnauthorized = true; - break; - default: - throw new Error(`Unknown ssl verificationMode: ${verificationMode}`); - } + if (config.ssl) { + const verificationMode = config.ssl.verificationMode; + switch (verificationMode) { + case 'none': + sslConfig.rejectUnauthorized = false; + break; + case 'certificate': + sslConfig.rejectUnauthorized = true; - const { certificateAuthorities } = readCertificateAuthorities(config); + // by default, NodeJS is checking the server identify + sslConfig.checkServerIdentity = () => undefined; + break; + case 'full': + sslConfig.rejectUnauthorized = true; + break; + default: + throw new Error(`Unknown ssl verificationMode: ${verificationMode}`); + } - sslConfig.ca = certificateAuthorities || []; + const { certificateAuthorities } = readCertificateAuthorities(config); + + sslConfig.ca = certificateAuthorities || []; + } const clientOptions: ClientOptions = { node: endpoint, From 8782afe64c664cd7c26449851bcbcbb98ec0c156 Mon Sep 17 00:00:00 2001 From: Craig Perkins Date: Mon, 18 Mar 2024 14:52:17 -0400 Subject: [PATCH 4/7] Add tls config to legacy client Signed-off-by: Craig Perkins --- .../server/client/client_config.ts | 2 +- .../server/legacy/client_config.test.ts | 65 +++++++++++++++++-- .../server/legacy/client_config.ts | 42 +++++++++++- 3 files changed, 98 insertions(+), 11 deletions(-) diff --git a/src/plugins/data_source/server/client/client_config.ts b/src/plugins/data_source/server/client/client_config.ts index 174f8371d2dc..52411f385273 100644 --- a/src/plugins/data_source/server/client/client_config.ts +++ b/src/plugins/data_source/server/client/client_config.ts @@ -66,7 +66,7 @@ export function parseClientOptions( return clientOptions; } -const readCertificateAuthorities = (rawConfig: any) => { +export const readCertificateAuthorities = (rawConfig: any) => { let certificateAuthorities: string[] | undefined; const addCAs = (ca: string[] | undefined) => { diff --git a/src/plugins/data_source/server/legacy/client_config.test.ts b/src/plugins/data_source/server/legacy/client_config.test.ts index a15143ecf69f..d0d1b3a95220 100644 --- a/src/plugins/data_source/server/legacy/client_config.test.ts +++ b/src/plugins/data_source/server/legacy/client_config.test.ts @@ -5,17 +5,20 @@ import { DataSourcePluginConfigType } from '../../config'; import { parseClientOptions } from './client_config'; -const TEST_DATA_SOURCE_ENDPOINT = 'http://test.com/'; +jest.mock('fs'); +const mockReadFileSync: jest.Mock = jest.requireMock('fs').readFileSync; -const config = { - enabled: true, - clientPool: { - size: 5, - }, -} as DataSourcePluginConfigType; +const TEST_DATA_SOURCE_ENDPOINT = 'http://test.com/'; describe('parseClientOptions', () => { test('include the ssl client configs as defaults', () => { + const config = { + enabled: true, + clientPool: { + size: 5, + }, + } as DataSourcePluginConfigType; + expect(parseClientOptions(config, TEST_DATA_SOURCE_ENDPOINT)).toEqual( expect.objectContaining({ host: TEST_DATA_SOURCE_ENDPOINT, @@ -25,4 +28,52 @@ describe('parseClientOptions', () => { }) ); }); + + test('test ssl config with verification mode set to none', () => { + const config = { + enabled: true, + ssl: { + verificationMode: 'none', + }, + clientPool: { + size: 5, + }, + } as DataSourcePluginConfigType; + expect(parseClientOptions(config, TEST_DATA_SOURCE_ENDPOINT)).toEqual( + expect.objectContaining({ + host: TEST_DATA_SOURCE_ENDPOINT, + ssl: { + rejectUnauthorized: false, + ca: [], + }, + }) + ); + }); + + test('test ssl config with verification mode set to full', () => { + const config = { + enabled: true, + ssl: { + verificationMode: 'full', + certificateAuthorities: ['some-path'], + }, + clientPool: { + size: 5, + }, + } as DataSourcePluginConfigType; + mockReadFileSync.mockReset(); + mockReadFileSync.mockImplementation((path: string) => `content-of-${path}`); + const parsedConfig = parseClientOptions(config, TEST_DATA_SOURCE_ENDPOINT); + expect(mockReadFileSync).toHaveBeenCalledTimes(1); + mockReadFileSync.mockClear(); + expect(parsedConfig).toEqual( + expect.objectContaining({ + host: TEST_DATA_SOURCE_ENDPOINT, + ssl: { + rejectUnauthorized: true, + ca: ['content-of-some-path'], + }, + }) + ); + }); }); diff --git a/src/plugins/data_source/server/legacy/client_config.ts b/src/plugins/data_source/server/legacy/client_config.ts index eed052cf245d..2ef29f0e4d51 100644 --- a/src/plugins/data_source/server/legacy/client_config.ts +++ b/src/plugins/data_source/server/legacy/client_config.ts @@ -4,7 +4,17 @@ */ import { ConfigOptions } from 'elasticsearch'; +import { checkServerIdentity } from 'tls'; import { DataSourcePluginConfigType } from '../../config'; +import { readCertificateAuthorities } from '../client/client_config'; + +/** @internal */ +type LegacyDataSourceSSLConfigOptions = Partial<{ + requestCert: boolean; + rejectUnauthorized: boolean; + checkServerIdentity: typeof checkServerIdentity; + ca: string[]; +}>; /** * Parse the client options from given data source config and endpoint @@ -18,11 +28,37 @@ export function parseClientOptions( endpoint: string, registeredSchema: any[] ): ConfigOptions { + const sslConfig: LegacyDataSourceSSLConfigOptions = { + rejectUnauthorized: true, + }; + + if (config.ssl) { + const verificationMode = config.ssl.verificationMode; + switch (verificationMode) { + case 'none': + sslConfig.rejectUnauthorized = false; + break; + case 'certificate': + sslConfig.rejectUnauthorized = true; + + // by default, NodeJS is checking the server identify + sslConfig.checkServerIdentity = () => undefined; + break; + case 'full': + sslConfig.rejectUnauthorized = true; + break; + default: + throw new Error(`Unknown ssl verificationMode: ${verificationMode}`); + } + + const { certificateAuthorities } = readCertificateAuthorities(config); + + sslConfig.ca = certificateAuthorities || []; + } + const configOptions: ConfigOptions = { host: endpoint, - ssl: { - rejectUnauthorized: true, - }, + ssl: sslConfig, plugins: registeredSchema, }; From f2851f639d60e512d951fa7b67e0f520a95c54d4 Mon Sep 17 00:00:00 2001 From: Craig Perkins Date: Tue, 19 Mar 2024 11:54:50 -0400 Subject: [PATCH 5/7] Add test for certificate mode Signed-off-by: Craig Perkins --- config/opensearch_dashboards.yml | 1 + .../server/client/client_config.test.ts | 30 +++++++++++++++++++ .../server/legacy/client_config.test.ts | 29 ++++++++++++++++++ 3 files changed, 60 insertions(+) diff --git a/config/opensearch_dashboards.yml b/config/opensearch_dashboards.yml index b5025f3a4bd3..40d643b014fd 100644 --- a/config/opensearch_dashboards.yml +++ b/config/opensearch_dashboards.yml @@ -286,6 +286,7 @@ #data_source.ssl.certificateAuthorities: [ "/path/to/your/CA.pem" ] # To disregard the validity of SSL certificates for connected data sources, change this setting's value to 'none'. +# Possible values include full, certificate and none #data_source.ssl.verificationMode: full # Set enabled false to hide authentication method in OpenSearch Dashboards. diff --git a/src/plugins/data_source/server/client/client_config.test.ts b/src/plugins/data_source/server/client/client_config.test.ts index e81677b49ae8..e6aef818f7de 100644 --- a/src/plugins/data_source/server/client/client_config.test.ts +++ b/src/plugins/data_source/server/client/client_config.test.ts @@ -52,6 +52,36 @@ describe('parseClientOptions', () => { ); }); + test('test ssl config with verification mode set to certificate', () => { + const config = { + enabled: true, + ssl: { + verificationMode: 'certificate', + certificateAuthorities: ['some-path'], + }, + clientPool: { + size: 5, + }, + } as DataSourcePluginConfigType; + mockReadFileSync.mockReset(); + mockReadFileSync.mockImplementation((path: string) => `content-of-${path}`); + const parsedConfig = parseClientOptions(config, TEST_DATA_SOURCE_ENDPOINT); + expect(mockReadFileSync).toHaveBeenCalledTimes(1); + mockReadFileSync.mockClear(); + expect(parsedConfig).toEqual( + expect.objectContaining({ + node: TEST_DATA_SOURCE_ENDPOINT, + ssl: { + requestCert: true, + rejectUnauthorized: true, + checkServerIdentity: expect.any(Function), + ca: ['content-of-some-path'], + }, + }) + ); + expect(parsedConfig.ssl?.checkServerIdentity()).toBeUndefined(); + }); + test('test ssl config with verification mode set to full', () => { const config = { enabled: true, diff --git a/src/plugins/data_source/server/legacy/client_config.test.ts b/src/plugins/data_source/server/legacy/client_config.test.ts index d0d1b3a95220..67445a686f90 100644 --- a/src/plugins/data_source/server/legacy/client_config.test.ts +++ b/src/plugins/data_source/server/legacy/client_config.test.ts @@ -50,6 +50,35 @@ describe('parseClientOptions', () => { ); }); + test('test ssl config with verification mode set to certificate', () => { + const config = { + enabled: true, + ssl: { + verificationMode: 'certificate', + certificateAuthorities: ['some-path'], + }, + clientPool: { + size: 5, + }, + } as DataSourcePluginConfigType; + mockReadFileSync.mockReset(); + mockReadFileSync.mockImplementation((path: string) => `content-of-${path}`); + const parsedConfig = parseClientOptions(config, TEST_DATA_SOURCE_ENDPOINT); + expect(mockReadFileSync).toHaveBeenCalledTimes(1); + mockReadFileSync.mockClear(); + expect(parsedConfig).toEqual( + expect.objectContaining({ + host: TEST_DATA_SOURCE_ENDPOINT, + ssl: { + rejectUnauthorized: true, + checkServerIdentity: expect.any(Function), + ca: ['content-of-some-path'], + }, + }) + ); + expect(parsedConfig.ssl?.checkServerIdentity()).toBeUndefined(); + }); + test('test ssl config with verification mode set to full', () => { const config = { enabled: true, From 6110ba02e15daa4ed89448bed6b61157bc70ac54 Mon Sep 17 00:00:00 2001 From: Craig Perkins Date: Wed, 20 Mar 2024 10:28:37 -0400 Subject: [PATCH 6/7] Respond to PR feedback Signed-off-by: Craig Perkins --- .../server/client/client_config.ts | 20 ++++++++++--------- .../server/legacy/client_config.ts | 4 +++- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/plugins/data_source/server/client/client_config.ts b/src/plugins/data_source/server/client/client_config.ts index 52411f385273..7903d3e2322e 100644 --- a/src/plugins/data_source/server/client/client_config.ts +++ b/src/plugins/data_source/server/client/client_config.ts @@ -52,7 +52,9 @@ export function parseClientOptions( throw new Error(`Unknown ssl verificationMode: ${verificationMode}`); } - const { certificateAuthorities } = readCertificateAuthorities(config); + const { certificateAuthorities } = readCertificateAuthorities( + config.ssl?.certificateAuthorities + ); sslConfig.ca = certificateAuthorities || []; } @@ -66,25 +68,25 @@ export function parseClientOptions( return clientOptions; } -export const readCertificateAuthorities = (rawConfig: any) => { +export const readCertificateAuthorities = ( + listOfCertificateAuthorities: string | string[] | undefined +) => { let certificateAuthorities: string[] | undefined; - const addCAs = (ca: string[] | undefined) => { + const addCertificateAuthorities = (ca: string[]) => { if (ca && ca.length) { certificateAuthorities = [...(certificateAuthorities || []), ...ca]; } }; - const ca = rawConfig.ssl.certificateAuthorities; + const ca = listOfCertificateAuthorities; if (ca) { const parsed: string[] = []; const paths = Array.isArray(ca) ? ca : [ca]; - if (paths.length > 0) { - for (const path of paths) { - parsed.push(readFile(path)); - } - addCAs(parsed); + for (const path of paths) { + parsed.push(readFile(path)); } + addCertificateAuthorities(parsed); } return { diff --git a/src/plugins/data_source/server/legacy/client_config.ts b/src/plugins/data_source/server/legacy/client_config.ts index 2ef29f0e4d51..97e570db343d 100644 --- a/src/plugins/data_source/server/legacy/client_config.ts +++ b/src/plugins/data_source/server/legacy/client_config.ts @@ -51,7 +51,9 @@ export function parseClientOptions( throw new Error(`Unknown ssl verificationMode: ${verificationMode}`); } - const { certificateAuthorities } = readCertificateAuthorities(config); + const { certificateAuthorities } = readCertificateAuthorities( + config.ssl?.certificateAuthorities + ); sslConfig.ca = certificateAuthorities || []; } From 14664fa90c1f13741ac1da80e38eaacdf940c9f4 Mon Sep 17 00:00:00 2001 From: Craig Perkins Date: Thu, 21 Mar 2024 09:29:57 -0400 Subject: [PATCH 7/7] Extract readCertificateAuthorities to util file and add more tests Signed-off-by: Craig Perkins --- .../server/client/client_config.ts | 32 +--------- .../server/legacy/client_config.ts | 2 +- .../server/util/tls_settings_provider.test.ts | 58 +++++++++++++++++++ .../server/util/tls_settings_provider.ts | 36 ++++++++++++ 4 files changed, 96 insertions(+), 32 deletions(-) create mode 100644 src/plugins/data_source/server/util/tls_settings_provider.test.ts create mode 100644 src/plugins/data_source/server/util/tls_settings_provider.ts diff --git a/src/plugins/data_source/server/client/client_config.ts b/src/plugins/data_source/server/client/client_config.ts index 7903d3e2322e..f77986810f1b 100644 --- a/src/plugins/data_source/server/client/client_config.ts +++ b/src/plugins/data_source/server/client/client_config.ts @@ -4,9 +4,9 @@ */ import { ClientOptions } from '@opensearch-project/opensearch'; -import { readFileSync } from 'fs'; import { checkServerIdentity } from 'tls'; import { DataSourcePluginConfigType } from '../../config'; +import { readCertificateAuthorities } from '../util/tls_settings_provider'; /** @internal */ type DataSourceSSLConfigOptions = Partial<{ @@ -67,33 +67,3 @@ export function parseClientOptions( return clientOptions; } - -export const readCertificateAuthorities = ( - listOfCertificateAuthorities: string | string[] | undefined -) => { - let certificateAuthorities: string[] | undefined; - - const addCertificateAuthorities = (ca: string[]) => { - if (ca && ca.length) { - certificateAuthorities = [...(certificateAuthorities || []), ...ca]; - } - }; - - const ca = listOfCertificateAuthorities; - if (ca) { - const parsed: string[] = []; - const paths = Array.isArray(ca) ? ca : [ca]; - for (const path of paths) { - parsed.push(readFile(path)); - } - addCertificateAuthorities(parsed); - } - - return { - certificateAuthorities, - }; -}; - -const readFile = (file: string) => { - return readFileSync(file, 'utf8'); -}; diff --git a/src/plugins/data_source/server/legacy/client_config.ts b/src/plugins/data_source/server/legacy/client_config.ts index 97e570db343d..a3704d3ec099 100644 --- a/src/plugins/data_source/server/legacy/client_config.ts +++ b/src/plugins/data_source/server/legacy/client_config.ts @@ -6,7 +6,7 @@ import { ConfigOptions } from 'elasticsearch'; import { checkServerIdentity } from 'tls'; import { DataSourcePluginConfigType } from '../../config'; -import { readCertificateAuthorities } from '../client/client_config'; +import { readCertificateAuthorities } from '../util/tls_settings_provider'; /** @internal */ type LegacyDataSourceSSLConfigOptions = Partial<{ diff --git a/src/plugins/data_source/server/util/tls_settings_provider.test.ts b/src/plugins/data_source/server/util/tls_settings_provider.test.ts new file mode 100644 index 000000000000..3458ea8e6ccf --- /dev/null +++ b/src/plugins/data_source/server/util/tls_settings_provider.test.ts @@ -0,0 +1,58 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ +import { readCertificateAuthorities } from './tls_settings_provider'; + +jest.mock('fs'); +const mockReadFileSync: jest.Mock = jest.requireMock('fs').readFileSync; + +describe('readCertificateAuthorities', () => { + test('test readCertificateAuthorities with list of paths', () => { + const ca: string[] = ['some-path']; + mockReadFileSync.mockReset(); + mockReadFileSync.mockImplementation((path: string) => `content-of-${path}`); + const certificateAuthorities = readCertificateAuthorities(ca); + expect(mockReadFileSync).toHaveBeenCalledTimes(1); + mockReadFileSync.mockClear(); + expect(certificateAuthorities).toEqual({ + certificateAuthorities: ['content-of-some-path'], + }); + }); + + test('test readCertificateAuthorities with single path', () => { + const ca: string = 'some-path'; + mockReadFileSync.mockReset(); + mockReadFileSync.mockImplementation((path: string) => `content-of-${path}`); + const certificateAuthorities = readCertificateAuthorities(ca); + expect(mockReadFileSync).toHaveBeenCalledTimes(1); + mockReadFileSync.mockClear(); + expect(certificateAuthorities).toEqual({ + certificateAuthorities: ['content-of-some-path'], + }); + }); + + test('test readCertificateAuthorities empty list', () => { + const ca: string[] = []; + mockReadFileSync.mockReset(); + mockReadFileSync.mockImplementation((path: string) => `content-of-${path}`); + const certificateAuthorities = readCertificateAuthorities(ca); + expect(mockReadFileSync).toHaveBeenCalledTimes(0); + mockReadFileSync.mockClear(); + expect(certificateAuthorities).toEqual({ + certificateAuthorities: [], + }); + }); + + test('test readCertificateAuthorities undefined', () => { + const ca = undefined; + mockReadFileSync.mockReset(); + mockReadFileSync.mockImplementation((path: string) => `content-of-${path}`); + const certificateAuthorities = readCertificateAuthorities(ca); + expect(mockReadFileSync).toHaveBeenCalledTimes(0); + mockReadFileSync.mockClear(); + expect(certificateAuthorities).toEqual({ + certificateAuthorities: [], + }); + }); +}); diff --git a/src/plugins/data_source/server/util/tls_settings_provider.ts b/src/plugins/data_source/server/util/tls_settings_provider.ts new file mode 100644 index 000000000000..0924041a756d --- /dev/null +++ b/src/plugins/data_source/server/util/tls_settings_provider.ts @@ -0,0 +1,36 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { readFileSync } from 'fs'; + +export const readCertificateAuthorities = ( + listOfCertificateAuthorities: string | string[] | undefined +) => { + let certificateAuthorities: string[] | undefined = []; + + const addCertificateAuthorities = (ca: string[]) => { + if (ca && ca.length) { + certificateAuthorities = [...(certificateAuthorities || []), ...ca]; + } + }; + + const ca = listOfCertificateAuthorities; + if (ca) { + const parsed: string[] = []; + const paths = Array.isArray(ca) ? ca : [ca]; + for (const path of paths) { + parsed.push(readFile(path)); + } + addCertificateAuthorities(parsed); + } + + return { + certificateAuthorities, + }; +}; + +const readFile = (file: string) => { + return readFileSync(file, 'utf8'); +};