Deprecate using elasticsearch.ssl.certificate without elasticsearch.ssl.key and vice versa (#54392)

This commit is contained in:
Joe Portner 2020-01-11 15:05:28 -05:00 committed by GitHub
parent 8ef560902b
commit 80b6dd8e15
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 208 additions and 187 deletions

View file

@ -208,35 +208,4 @@ describe('core deprecations', () => {
).toEqual([`worker-src blob:`]);
});
});
describe('elasticsearchUsernameDeprecation', () => {
it('logs a warning if elasticsearch.username is set to "elastic"', () => {
const { messages } = applyCoreDeprecations({
elasticsearch: {
username: 'elastic',
},
});
expect(messages).toMatchInlineSnapshot(`
Array [
"Setting elasticsearch.username to \\"elastic\\" is deprecated. You should use the \\"kibana\\" user instead.",
]
`);
});
it('does not log a warning if elasticsearch.username is set to something besides "elastic"', () => {
const { messages } = applyCoreDeprecations({
elasticsearch: {
username: 'otheruser',
},
});
expect(messages).toHaveLength(0);
});
it('does not log a warning if elasticsearch.username is unset', () => {
const { messages } = applyCoreDeprecations({
elasticsearch: {},
});
expect(messages).toHaveLength(0);
});
});
});

View file

@ -91,16 +91,6 @@ const cspRulesDeprecation: ConfigDeprecation = (settings, fromPath, log) => {
return settings;
};
const elasticsearchUsernameDeprecation: ConfigDeprecation = (settings, _fromPath, log) => {
const username: string | undefined = get(settings, 'elasticsearch.username');
if (username === 'elastic') {
log(
`Setting elasticsearch.username to "elastic" is deprecated. You should use the "kibana" user instead.`
);
}
return settings;
};
export const coreDeprecationProvider: ConfigDeprecationProvider = ({
unusedFromRoot,
renameFromRoot,
@ -120,5 +110,4 @@ export const coreDeprecationProvider: ConfigDeprecationProvider = ({
dataPathDeprecation,
rewriteBasePathDeprecation,
cspRulesDeprecation,
elasticsearchUsernameDeprecation,
];

View file

@ -23,19 +23,32 @@ import {
mockReadPkcs12Truststore,
} from './elasticsearch_config.test.mocks';
import { ElasticsearchConfig, config, ElasticsearchConfigType } from './elasticsearch_config';
import { loggingServiceMock } from '../mocks';
import { Logger } from '../logging';
import { ElasticsearchConfig, config } from './elasticsearch_config';
import { applyDeprecations, configDeprecationFactory } from '../config/deprecation';
const createElasticsearchConfig = (rawConfig: ElasticsearchConfigType, log?: Logger) => {
if (!log) {
log = loggingServiceMock.create().get('config');
}
return new ElasticsearchConfig(rawConfig, log);
const CONFIG_PATH = 'elasticsearch';
const applyElasticsearchDeprecations = (settings: Record<string, any> = {}) => {
const deprecations = config.deprecations!(configDeprecationFactory);
const deprecationMessages: string[] = [];
const _config: any = {};
_config[CONFIG_PATH] = settings;
const migrated = applyDeprecations(
_config,
deprecations.map(deprecation => ({
deprecation,
path: CONFIG_PATH,
})),
msg => deprecationMessages.push(msg)
);
return {
messages: deprecationMessages,
migrated,
};
};
test('set correct defaults', () => {
const configValue = createElasticsearchConfig(config.schema.validate({}));
const configValue = new ElasticsearchConfig(config.schema.validate({}));
expect(configValue).toMatchInlineSnapshot(`
ElasticsearchConfig {
"apiVersion": "master",
@ -70,17 +83,17 @@ test('set correct defaults', () => {
});
test('#hosts accepts both string and array of strings', () => {
let configValue = createElasticsearchConfig(
let configValue = new ElasticsearchConfig(
config.schema.validate({ hosts: 'http://some.host:1234' })
);
expect(configValue.hosts).toEqual(['http://some.host:1234']);
configValue = createElasticsearchConfig(
configValue = new ElasticsearchConfig(
config.schema.validate({ hosts: ['http://some.host:1234'] })
);
expect(configValue.hosts).toEqual(['http://some.host:1234']);
configValue = createElasticsearchConfig(
configValue = new ElasticsearchConfig(
config.schema.validate({
hosts: ['http://some.host:1234', 'https://some.another.host'],
})
@ -89,17 +102,17 @@ test('#hosts accepts both string and array of strings', () => {
});
test('#requestHeadersWhitelist accepts both string and array of strings', () => {
let configValue = createElasticsearchConfig(
let configValue = new ElasticsearchConfig(
config.schema.validate({ requestHeadersWhitelist: 'token' })
);
expect(configValue.requestHeadersWhitelist).toEqual(['token']);
configValue = createElasticsearchConfig(
configValue = new ElasticsearchConfig(
config.schema.validate({ requestHeadersWhitelist: ['token'] })
);
expect(configValue.requestHeadersWhitelist).toEqual(['token']);
configValue = createElasticsearchConfig(
configValue = new ElasticsearchConfig(
config.schema.validate({
requestHeadersWhitelist: ['token', 'X-Forwarded-Proto'],
})
@ -122,7 +135,7 @@ describe('reads files', () => {
});
it('reads certificate authorities when ssl.keystore.path is specified', () => {
const configValue = createElasticsearchConfig(
const configValue = new ElasticsearchConfig(
config.schema.validate({ ssl: { keystore: { path: 'some-path' } } })
);
expect(mockReadPkcs12Keystore).toHaveBeenCalledTimes(1);
@ -130,7 +143,7 @@ describe('reads files', () => {
});
it('reads certificate authorities when ssl.truststore.path is specified', () => {
const configValue = createElasticsearchConfig(
const configValue = new ElasticsearchConfig(
config.schema.validate({ ssl: { truststore: { path: 'some-path' } } })
);
expect(mockReadPkcs12Truststore).toHaveBeenCalledTimes(1);
@ -138,21 +151,21 @@ describe('reads files', () => {
});
it('reads certificate authorities when ssl.certificateAuthorities is specified', () => {
let configValue = createElasticsearchConfig(
let configValue = new ElasticsearchConfig(
config.schema.validate({ ssl: { certificateAuthorities: 'some-path' } })
);
expect(mockReadFileSync).toHaveBeenCalledTimes(1);
expect(configValue.ssl.certificateAuthorities).toEqual(['content-of-some-path']);
mockReadFileSync.mockClear();
configValue = createElasticsearchConfig(
configValue = new ElasticsearchConfig(
config.schema.validate({ ssl: { certificateAuthorities: ['some-path'] } })
);
expect(mockReadFileSync).toHaveBeenCalledTimes(1);
expect(configValue.ssl.certificateAuthorities).toEqual(['content-of-some-path']);
mockReadFileSync.mockClear();
configValue = createElasticsearchConfig(
configValue = new ElasticsearchConfig(
config.schema.validate({
ssl: { certificateAuthorities: ['some-path', 'another-path'] },
})
@ -165,7 +178,7 @@ describe('reads files', () => {
});
it('reads certificate authorities when ssl.keystore.path, ssl.truststore.path, and ssl.certificateAuthorities are specified', () => {
const configValue = createElasticsearchConfig(
const configValue = new ElasticsearchConfig(
config.schema.validate({
ssl: {
keystore: { path: 'some-path' },
@ -185,7 +198,7 @@ describe('reads files', () => {
});
it('reads a private key and certificate when ssl.keystore.path is specified', () => {
const configValue = createElasticsearchConfig(
const configValue = new ElasticsearchConfig(
config.schema.validate({ ssl: { keystore: { path: 'some-path' } } })
);
expect(mockReadPkcs12Keystore).toHaveBeenCalledTimes(1);
@ -194,7 +207,7 @@ describe('reads files', () => {
});
it('reads a private key when ssl.key is specified', () => {
const configValue = createElasticsearchConfig(
const configValue = new ElasticsearchConfig(
config.schema.validate({ ssl: { key: 'some-path' } })
);
expect(mockReadFileSync).toHaveBeenCalledTimes(1);
@ -202,7 +215,7 @@ describe('reads files', () => {
});
it('reads a certificate when ssl.certificate is specified', () => {
const configValue = createElasticsearchConfig(
const configValue = new ElasticsearchConfig(
config.schema.validate({ ssl: { certificate: 'some-path' } })
);
expect(mockReadFileSync).toHaveBeenCalledTimes(1);
@ -225,8 +238,8 @@ describe('throws when config is invalid', () => {
it('throws if key is invalid', () => {
const value = { ssl: { key: '/invalid/key' } };
expect(() =>
createElasticsearchConfig(config.schema.validate(value))
expect(
() => new ElasticsearchConfig(config.schema.validate(value))
).toThrowErrorMatchingInlineSnapshot(
`"ENOENT: no such file or directory, open '/invalid/key'"`
);
@ -234,8 +247,8 @@ describe('throws when config is invalid', () => {
it('throws if certificate is invalid', () => {
const value = { ssl: { certificate: '/invalid/cert' } };
expect(() =>
createElasticsearchConfig(config.schema.validate(value))
expect(
() => new ElasticsearchConfig(config.schema.validate(value))
).toThrowErrorMatchingInlineSnapshot(
`"ENOENT: no such file or directory, open '/invalid/cert'"`
);
@ -243,34 +256,40 @@ describe('throws when config is invalid', () => {
it('throws if certificateAuthorities is invalid', () => {
const value = { ssl: { certificateAuthorities: '/invalid/ca' } };
expect(() =>
createElasticsearchConfig(config.schema.validate(value))
expect(
() => new ElasticsearchConfig(config.schema.validate(value))
).toThrowErrorMatchingInlineSnapshot(`"ENOENT: no such file or directory, open '/invalid/ca'"`);
});
it('throws if keystore path is invalid', () => {
const value = { ssl: { keystore: { path: '/invalid/keystore' } } };
expect(() =>
createElasticsearchConfig(config.schema.validate(value))
expect(
() => new ElasticsearchConfig(config.schema.validate(value))
).toThrowErrorMatchingInlineSnapshot(
`"ENOENT: no such file or directory, open '/invalid/keystore'"`
);
});
it('throws if keystore does not contain a key or certificate', () => {
it('throws if keystore does not contain a key', () => {
mockReadPkcs12Keystore.mockReturnValueOnce({});
const value = { ssl: { keystore: { path: 'some-path' } } };
expect(() =>
createElasticsearchConfig(config.schema.validate(value))
).toThrowErrorMatchingInlineSnapshot(
`"Did not find key or certificate in Elasticsearch keystore."`
);
expect(
() => new ElasticsearchConfig(config.schema.validate(value))
).toThrowErrorMatchingInlineSnapshot(`"Did not find key in Elasticsearch keystore."`);
});
it('throws if keystore does not contain a certificate', () => {
mockReadPkcs12Keystore.mockReturnValueOnce({ key: 'foo' });
const value = { ssl: { keystore: { path: 'some-path' } } };
expect(
() => new ElasticsearchConfig(config.schema.validate(value))
).toThrowErrorMatchingInlineSnapshot(`"Did not find certificate in Elasticsearch keystore."`);
});
it('throws if truststore path is invalid', () => {
const value = { ssl: { keystore: { path: '/invalid/truststore' } } };
expect(() =>
createElasticsearchConfig(config.schema.validate(value))
expect(
() => new ElasticsearchConfig(config.schema.validate(value))
).toThrowErrorMatchingInlineSnapshot(
`"ENOENT: no such file or directory, open '/invalid/truststore'"`
);
@ -291,31 +310,47 @@ describe('throws when config is invalid', () => {
});
});
describe('logs warnings', () => {
let logger: ReturnType<typeof loggingServiceMock.create>;
let log: Logger;
beforeAll(() => {
mockReadFileSync.mockResolvedValue('foo');
describe('deprecations', () => {
it('logs a warning if elasticsearch.username is set to "elastic"', () => {
const { messages } = applyElasticsearchDeprecations({ username: 'elastic' });
expect(messages).toMatchInlineSnapshot(`
Array [
"Setting [${CONFIG_PATH}.username] to \\"elastic\\" is deprecated. You should use the \\"kibana\\" user instead.",
]
`);
});
beforeEach(() => {
logger = loggingServiceMock.create();
log = logger.get('config');
it('does not log a warning if elasticsearch.username is set to something besides "elastic"', () => {
const { messages } = applyElasticsearchDeprecations({ username: 'otheruser' });
expect(messages).toHaveLength(0);
});
it('warns if ssl.key is set and ssl.certificate is not', () => {
createElasticsearchConfig(config.schema.validate({ ssl: { key: 'some-path' } }), log);
expect(loggingServiceMock.collect(logger).warn[0][0]).toMatchInlineSnapshot(
`"Detected a key without a certificate; mutual TLS authentication is disabled."`
);
it('does not log a warning if elasticsearch.username is unset', () => {
const { messages } = applyElasticsearchDeprecations({});
expect(messages).toHaveLength(0);
});
it('warns if ssl.certificate is set and ssl.key is not', () => {
createElasticsearchConfig(config.schema.validate({ ssl: { certificate: 'some-path' } }), log);
expect(loggingServiceMock.collect(logger).warn[0][0]).toMatchInlineSnapshot(
`"Detected a certificate without a key; mutual TLS authentication is disabled."`
);
it('logs a warning if ssl.key is set and ssl.certificate is not', () => {
const { messages } = applyElasticsearchDeprecations({ ssl: { key: '' } });
expect(messages).toMatchInlineSnapshot(`
Array [
"Setting [${CONFIG_PATH}.ssl.key] without [${CONFIG_PATH}.ssl.certificate] is deprecated. This has no effect, you should use both settings to enable TLS client authentication to Elasticsearch.",
]
`);
});
it('logs a warning if ssl.certificate is set and ssl.key is not', () => {
const { messages } = applyElasticsearchDeprecations({ ssl: { certificate: '' } });
expect(messages).toMatchInlineSnapshot(`
Array [
"Setting [${CONFIG_PATH}.ssl.certificate] without [${CONFIG_PATH}.ssl.key] is deprecated. This has no effect, you should use both settings to enable TLS client authentication to Elasticsearch.",
]
`);
});
it('does not log a warning if both ssl.key and ssl.certificate are set', () => {
const { messages } = applyElasticsearchDeprecations({ ssl: { key: '', certificate: '' } });
expect(messages).toEqual([]);
});
});

View file

@ -20,92 +20,120 @@
import { schema, TypeOf } from '@kbn/config-schema';
import { Duration } from 'moment';
import { readFileSync } from 'fs';
import { ConfigDeprecationProvider } from 'src/core/server';
import { readPkcs12Keystore, readPkcs12Truststore } from '../../utils';
import { Logger } from '../logging';
import { ServiceConfigDescriptor } from '../internal_types';
const hostURISchema = schema.uri({ scheme: ['http', 'https'] });
export const DEFAULT_API_VERSION = 'master';
export type ElasticsearchConfigType = TypeOf<typeof config.schema>;
export type ElasticsearchConfigType = TypeOf<typeof configSchema>;
type SslConfigSchema = ElasticsearchConfigType['ssl'];
export const config = {
path: 'elasticsearch',
schema: schema.object({
sniffOnStart: schema.boolean({ defaultValue: false }),
sniffInterval: schema.oneOf([schema.duration(), schema.literal(false)], {
defaultValue: false,
}),
sniffOnConnectionFault: schema.boolean({ defaultValue: false }),
hosts: schema.oneOf([hostURISchema, schema.arrayOf(hostURISchema, { minSize: 1 })], {
defaultValue: 'http://localhost:9200',
}),
preserveHost: schema.boolean({ defaultValue: true }),
username: schema.maybe(
schema.conditional(
schema.contextRef('dist'),
false,
schema.string({
validate: rawConfig => {
if (rawConfig === 'elastic') {
return (
'value of "elastic" is forbidden. This is a superuser account that can obfuscate ' +
'privilege-related issues. You should use the "kibana" user instead.'
);
}
},
}),
schema.string()
)
),
password: schema.maybe(schema.string()),
requestHeadersWhitelist: schema.oneOf([schema.string(), schema.arrayOf(schema.string())], {
defaultValue: ['authorization'],
}),
customHeaders: schema.recordOf(schema.string(), schema.string(), { defaultValue: {} }),
shardTimeout: schema.duration({ defaultValue: '30s' }),
requestTimeout: schema.duration({ defaultValue: '30s' }),
pingTimeout: schema.duration({ defaultValue: schema.siblingRef('requestTimeout') }),
startupTimeout: schema.duration({ defaultValue: '5s' }),
logQueries: schema.boolean({ defaultValue: false }),
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 })])
),
certificate: schema.maybe(schema.string()),
key: schema.maybe(schema.string()),
keyPassphrase: schema.maybe(schema.string()),
keystore: schema.object({
path: schema.maybe(schema.string()),
password: schema.maybe(schema.string()),
}),
truststore: schema.object({
path: schema.maybe(schema.string()),
password: schema.maybe(schema.string()),
}),
alwaysPresentCertificate: schema.boolean({ defaultValue: false }),
},
{
const configSchema = schema.object({
sniffOnStart: schema.boolean({ defaultValue: false }),
sniffInterval: schema.oneOf([schema.duration(), schema.literal(false)], {
defaultValue: false,
}),
sniffOnConnectionFault: schema.boolean({ defaultValue: false }),
hosts: schema.oneOf([hostURISchema, schema.arrayOf(hostURISchema, { minSize: 1 })], {
defaultValue: 'http://localhost:9200',
}),
preserveHost: schema.boolean({ defaultValue: true }),
username: schema.maybe(
schema.conditional(
schema.contextRef('dist'),
false,
schema.string({
validate: rawConfig => {
if (rawConfig.key && rawConfig.keystore.path) {
return 'cannot use [key] when [keystore.path] is specified';
}
if (rawConfig.certificate && rawConfig.keystore.path) {
return 'cannot use [certificate] when [keystore.path] is specified';
if (rawConfig === 'elastic') {
return (
'value of "elastic" is forbidden. This is a superuser account that can obfuscate ' +
'privilege-related issues. You should use the "kibana" user instead.'
);
}
},
}
),
apiVersion: schema.string({ defaultValue: DEFAULT_API_VERSION }),
healthCheck: schema.object({ delay: schema.duration({ defaultValue: 2500 }) }),
ignoreVersionMismatch: schema.boolean({ defaultValue: false }),
}),
schema.string()
)
),
password: schema.maybe(schema.string()),
requestHeadersWhitelist: schema.oneOf([schema.string(), schema.arrayOf(schema.string())], {
defaultValue: ['authorization'],
}),
customHeaders: schema.recordOf(schema.string(), schema.string(), { defaultValue: {} }),
shardTimeout: schema.duration({ defaultValue: '30s' }),
requestTimeout: schema.duration({ defaultValue: '30s' }),
pingTimeout: schema.duration({ defaultValue: schema.siblingRef('requestTimeout') }),
startupTimeout: schema.duration({ defaultValue: '5s' }),
logQueries: schema.boolean({ defaultValue: false }),
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 })])
),
certificate: schema.maybe(schema.string()),
key: schema.maybe(schema.string()),
keyPassphrase: schema.maybe(schema.string()),
keystore: schema.object({
path: schema.maybe(schema.string()),
password: schema.maybe(schema.string()),
}),
truststore: schema.object({
path: schema.maybe(schema.string()),
password: schema.maybe(schema.string()),
}),
alwaysPresentCertificate: schema.boolean({ defaultValue: false }),
},
{
validate: rawConfig => {
if (rawConfig.key && rawConfig.keystore.path) {
return 'cannot use [key] when [keystore.path] is specified';
}
if (rawConfig.certificate && rawConfig.keystore.path) {
return 'cannot use [certificate] when [keystore.path] is specified';
}
},
}
),
apiVersion: schema.string({ defaultValue: DEFAULT_API_VERSION }),
healthCheck: schema.object({ delay: schema.duration({ defaultValue: 2500 }) }),
ignoreVersionMismatch: schema.boolean({ defaultValue: false }),
});
const deprecations: ConfigDeprecationProvider = () => [
(settings, fromPath, log) => {
const es = settings[fromPath];
if (!es) {
return settings;
}
if (es.username === 'elastic') {
log(
`Setting [${fromPath}.username] to "elastic" is deprecated. You should use the "kibana" user instead.`
);
}
if (es.ssl?.key !== undefined && es.ssl?.certificate === undefined) {
log(
`Setting [${fromPath}.ssl.key] without [${fromPath}.ssl.certificate] is deprecated. This has no effect, you should use both settings to enable TLS client authentication to Elasticsearch.`
);
} else if (es.ssl?.certificate !== undefined && es.ssl?.key === undefined) {
log(
`Setting [${fromPath}.ssl.certificate] without [${fromPath}.ssl.key] is deprecated. This has no effect, you should use both settings to enable TLS client authentication to Elasticsearch.`
);
}
return settings;
},
];
export const config: ServiceConfigDescriptor<ElasticsearchConfigType> = {
path: 'elasticsearch',
schema: configSchema,
deprecations,
};
export class ElasticsearchConfig {
@ -205,7 +233,7 @@ export class ElasticsearchConfig {
*/
public readonly customHeaders: ElasticsearchConfigType['customHeaders'];
constructor(rawConfig: ElasticsearchConfigType, log: Logger) {
constructor(rawConfig: ElasticsearchConfigType) {
this.ignoreVersionMismatch = rawConfig.ignoreVersionMismatch;
this.apiVersion = rawConfig.apiVersion;
this.logQueries = rawConfig.logQueries;
@ -227,12 +255,6 @@ export class ElasticsearchConfig {
const { alwaysPresentCertificate, verificationMode } = rawConfig.ssl;
const { key, keyPassphrase, certificate, certificateAuthorities } = readKeyAndCerts(rawConfig);
if (key && !certificate) {
log.warn(`Detected a key without a certificate; mutual TLS authentication is disabled.`);
} else if (certificate && !key) {
log.warn(`Detected a certificate without a key; mutual TLS authentication is disabled.`);
}
this.ssl = {
alwaysPresentCertificate,
key,
@ -261,8 +283,10 @@ const readKeyAndCerts = (rawConfig: ElasticsearchConfigType) => {
rawConfig.ssl.keystore.path,
rawConfig.ssl.keystore.password
);
if (!keystore.key && !keystore.cert) {
throw new Error(`Did not find key or certificate in Elasticsearch keystore.`);
if (!keystore.key) {
throw new Error(`Did not find key in Elasticsearch keystore.`);
} else if (!keystore.cert) {
throw new Error(`Did not find certificate in Elasticsearch keystore.`);
}
key = keystore.key;
certificate = keystore.cert;

View file

@ -52,7 +52,7 @@ export class ElasticsearchService implements CoreService<InternalElasticsearchSe
this.log = coreContext.logger.get('elasticsearch-service');
this.config$ = coreContext.configService
.atPath<ElasticsearchConfigType>('elasticsearch')
.pipe(map(rawConfig => new ElasticsearchConfig(rawConfig, coreContext.logger.get('config'))));
.pipe(map(rawConfig => new ElasticsearchConfig(rawConfig)));
}
public async setup(deps: SetupDeps): Promise<InternalElasticsearchServiceSetup> {

View file

@ -450,11 +450,11 @@ export interface AuthToolkit {
export class BasePath {
// @internal
constructor(serverBasePath?: string);
get: (request: KibanaRequest<unknown, unknown, unknown, any> | LegacyRequest) => string;
get: (request: LegacyRequest | KibanaRequest<unknown, unknown, unknown, any>) => string;
prepend: (path: string) => string;
remove: (path: string) => string;
readonly serverBasePath: string;
set: (request: KibanaRequest<unknown, unknown, unknown, any> | LegacyRequest, requestSpecificBasePath: string) => void;
set: (request: LegacyRequest | KibanaRequest<unknown, unknown, unknown, any>, requestSpecificBasePath: string) => void;
}
// Warning: (ae-forgotten-export) The symbol "BootstrapArgs" needs to be exported by the entry point index.d.ts

View file

@ -256,6 +256,10 @@ export class Server {
];
this.configService.addDeprecationProvider(rootConfigPath, coreDeprecationProvider);
this.configService.addDeprecationProvider(
elasticsearchConfig.path,
elasticsearchConfig.deprecations!
);
this.configService.addDeprecationProvider(
uiSettingsConfig.path,
uiSettingsConfig.deprecations!