Skip to content

Commit 80b6dd8

Browse files
authored
Deprecate using elasticsearch.ssl.certificate without elasticsearch.ssl.key and vice versa (#54392)
1 parent 8ef5609 commit 80b6dd8

File tree

7 files changed

+208
-187
lines changed

7 files changed

+208
-187
lines changed

src/core/server/config/deprecation/core_deprecations.test.ts

Lines changed: 0 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -208,35 +208,4 @@ describe('core deprecations', () => {
208208
).toEqual([`worker-src blob:`]);
209209
});
210210
});
211-
212-
describe('elasticsearchUsernameDeprecation', () => {
213-
it('logs a warning if elasticsearch.username is set to "elastic"', () => {
214-
const { messages } = applyCoreDeprecations({
215-
elasticsearch: {
216-
username: 'elastic',
217-
},
218-
});
219-
expect(messages).toMatchInlineSnapshot(`
220-
Array [
221-
"Setting elasticsearch.username to \\"elastic\\" is deprecated. You should use the \\"kibana\\" user instead.",
222-
]
223-
`);
224-
});
225-
226-
it('does not log a warning if elasticsearch.username is set to something besides "elastic"', () => {
227-
const { messages } = applyCoreDeprecations({
228-
elasticsearch: {
229-
username: 'otheruser',
230-
},
231-
});
232-
expect(messages).toHaveLength(0);
233-
});
234-
235-
it('does not log a warning if elasticsearch.username is unset', () => {
236-
const { messages } = applyCoreDeprecations({
237-
elasticsearch: {},
238-
});
239-
expect(messages).toHaveLength(0);
240-
});
241-
});
242211
});

src/core/server/config/deprecation/core_deprecations.ts

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -91,16 +91,6 @@ const cspRulesDeprecation: ConfigDeprecation = (settings, fromPath, log) => {
9191
return settings;
9292
};
9393

94-
const elasticsearchUsernameDeprecation: ConfigDeprecation = (settings, _fromPath, log) => {
95-
const username: string | undefined = get(settings, 'elasticsearch.username');
96-
if (username === 'elastic') {
97-
log(
98-
`Setting elasticsearch.username to "elastic" is deprecated. You should use the "kibana" user instead.`
99-
);
100-
}
101-
return settings;
102-
};
103-
10494
export const coreDeprecationProvider: ConfigDeprecationProvider = ({
10595
unusedFromRoot,
10696
renameFromRoot,
@@ -120,5 +110,4 @@ export const coreDeprecationProvider: ConfigDeprecationProvider = ({
120110
dataPathDeprecation,
121111
rewriteBasePathDeprecation,
122112
cspRulesDeprecation,
123-
elasticsearchUsernameDeprecation,
124113
];

src/core/server/elasticsearch/elasticsearch_config.test.ts

Lines changed: 94 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -23,19 +23,32 @@ import {
2323
mockReadPkcs12Truststore,
2424
} from './elasticsearch_config.test.mocks';
2525

26-
import { ElasticsearchConfig, config, ElasticsearchConfigType } from './elasticsearch_config';
27-
import { loggingServiceMock } from '../mocks';
28-
import { Logger } from '../logging';
29-
30-
const createElasticsearchConfig = (rawConfig: ElasticsearchConfigType, log?: Logger) => {
31-
if (!log) {
32-
log = loggingServiceMock.create().get('config');
33-
}
34-
return new ElasticsearchConfig(rawConfig, log);
26+
import { ElasticsearchConfig, config } from './elasticsearch_config';
27+
import { applyDeprecations, configDeprecationFactory } from '../config/deprecation';
28+
29+
const CONFIG_PATH = 'elasticsearch';
30+
31+
const applyElasticsearchDeprecations = (settings: Record<string, any> = {}) => {
32+
const deprecations = config.deprecations!(configDeprecationFactory);
33+
const deprecationMessages: string[] = [];
34+
const _config: any = {};
35+
_config[CONFIG_PATH] = settings;
36+
const migrated = applyDeprecations(
37+
_config,
38+
deprecations.map(deprecation => ({
39+
deprecation,
40+
path: CONFIG_PATH,
41+
})),
42+
msg => deprecationMessages.push(msg)
43+
);
44+
return {
45+
messages: deprecationMessages,
46+
migrated,
47+
};
3548
};
3649

3750
test('set correct defaults', () => {
38-
const configValue = createElasticsearchConfig(config.schema.validate({}));
51+
const configValue = new ElasticsearchConfig(config.schema.validate({}));
3952
expect(configValue).toMatchInlineSnapshot(`
4053
ElasticsearchConfig {
4154
"apiVersion": "master",
@@ -70,17 +83,17 @@ test('set correct defaults', () => {
7083
});
7184

7285
test('#hosts accepts both string and array of strings', () => {
73-
let configValue = createElasticsearchConfig(
86+
let configValue = new ElasticsearchConfig(
7487
config.schema.validate({ hosts: 'http://some.host:1234' })
7588
);
7689
expect(configValue.hosts).toEqual(['http://some.host:1234']);
7790

78-
configValue = createElasticsearchConfig(
91+
configValue = new ElasticsearchConfig(
7992
config.schema.validate({ hosts: ['http://some.host:1234'] })
8093
);
8194
expect(configValue.hosts).toEqual(['http://some.host:1234']);
8295

83-
configValue = createElasticsearchConfig(
96+
configValue = new ElasticsearchConfig(
8497
config.schema.validate({
8598
hosts: ['http://some.host:1234', 'https://some.another.host'],
8699
})
@@ -89,17 +102,17 @@ test('#hosts accepts both string and array of strings', () => {
89102
});
90103

91104
test('#requestHeadersWhitelist accepts both string and array of strings', () => {
92-
let configValue = createElasticsearchConfig(
105+
let configValue = new ElasticsearchConfig(
93106
config.schema.validate({ requestHeadersWhitelist: 'token' })
94107
);
95108
expect(configValue.requestHeadersWhitelist).toEqual(['token']);
96109

97-
configValue = createElasticsearchConfig(
110+
configValue = new ElasticsearchConfig(
98111
config.schema.validate({ requestHeadersWhitelist: ['token'] })
99112
);
100113
expect(configValue.requestHeadersWhitelist).toEqual(['token']);
101114

102-
configValue = createElasticsearchConfig(
115+
configValue = new ElasticsearchConfig(
103116
config.schema.validate({
104117
requestHeadersWhitelist: ['token', 'X-Forwarded-Proto'],
105118
})
@@ -122,37 +135,37 @@ describe('reads files', () => {
122135
});
123136

124137
it('reads certificate authorities when ssl.keystore.path is specified', () => {
125-
const configValue = createElasticsearchConfig(
138+
const configValue = new ElasticsearchConfig(
126139
config.schema.validate({ ssl: { keystore: { path: 'some-path' } } })
127140
);
128141
expect(mockReadPkcs12Keystore).toHaveBeenCalledTimes(1);
129142
expect(configValue.ssl.certificateAuthorities).toEqual(['content-of-some-path.ca']);
130143
});
131144

132145
it('reads certificate authorities when ssl.truststore.path is specified', () => {
133-
const configValue = createElasticsearchConfig(
146+
const configValue = new ElasticsearchConfig(
134147
config.schema.validate({ ssl: { truststore: { path: 'some-path' } } })
135148
);
136149
expect(mockReadPkcs12Truststore).toHaveBeenCalledTimes(1);
137150
expect(configValue.ssl.certificateAuthorities).toEqual(['content-of-some-path']);
138151
});
139152

140153
it('reads certificate authorities when ssl.certificateAuthorities is specified', () => {
141-
let configValue = createElasticsearchConfig(
154+
let configValue = new ElasticsearchConfig(
142155
config.schema.validate({ ssl: { certificateAuthorities: 'some-path' } })
143156
);
144157
expect(mockReadFileSync).toHaveBeenCalledTimes(1);
145158
expect(configValue.ssl.certificateAuthorities).toEqual(['content-of-some-path']);
146159

147160
mockReadFileSync.mockClear();
148-
configValue = createElasticsearchConfig(
161+
configValue = new ElasticsearchConfig(
149162
config.schema.validate({ ssl: { certificateAuthorities: ['some-path'] } })
150163
);
151164
expect(mockReadFileSync).toHaveBeenCalledTimes(1);
152165
expect(configValue.ssl.certificateAuthorities).toEqual(['content-of-some-path']);
153166

154167
mockReadFileSync.mockClear();
155-
configValue = createElasticsearchConfig(
168+
configValue = new ElasticsearchConfig(
156169
config.schema.validate({
157170
ssl: { certificateAuthorities: ['some-path', 'another-path'] },
158171
})
@@ -165,7 +178,7 @@ describe('reads files', () => {
165178
});
166179

167180
it('reads certificate authorities when ssl.keystore.path, ssl.truststore.path, and ssl.certificateAuthorities are specified', () => {
168-
const configValue = createElasticsearchConfig(
181+
const configValue = new ElasticsearchConfig(
169182
config.schema.validate({
170183
ssl: {
171184
keystore: { path: 'some-path' },
@@ -185,7 +198,7 @@ describe('reads files', () => {
185198
});
186199

187200
it('reads a private key and certificate when ssl.keystore.path is specified', () => {
188-
const configValue = createElasticsearchConfig(
201+
const configValue = new ElasticsearchConfig(
189202
config.schema.validate({ ssl: { keystore: { path: 'some-path' } } })
190203
);
191204
expect(mockReadPkcs12Keystore).toHaveBeenCalledTimes(1);
@@ -194,15 +207,15 @@ describe('reads files', () => {
194207
});
195208

196209
it('reads a private key when ssl.key is specified', () => {
197-
const configValue = createElasticsearchConfig(
210+
const configValue = new ElasticsearchConfig(
198211
config.schema.validate({ ssl: { key: 'some-path' } })
199212
);
200213
expect(mockReadFileSync).toHaveBeenCalledTimes(1);
201214
expect(configValue.ssl.key).toEqual('content-of-some-path');
202215
});
203216

204217
it('reads a certificate when ssl.certificate is specified', () => {
205-
const configValue = createElasticsearchConfig(
218+
const configValue = new ElasticsearchConfig(
206219
config.schema.validate({ ssl: { certificate: 'some-path' } })
207220
);
208221
expect(mockReadFileSync).toHaveBeenCalledTimes(1);
@@ -225,52 +238,58 @@ describe('throws when config is invalid', () => {
225238

226239
it('throws if key is invalid', () => {
227240
const value = { ssl: { key: '/invalid/key' } };
228-
expect(() =>
229-
createElasticsearchConfig(config.schema.validate(value))
241+
expect(
242+
() => new ElasticsearchConfig(config.schema.validate(value))
230243
).toThrowErrorMatchingInlineSnapshot(
231244
`"ENOENT: no such file or directory, open '/invalid/key'"`
232245
);
233246
});
234247

235248
it('throws if certificate is invalid', () => {
236249
const value = { ssl: { certificate: '/invalid/cert' } };
237-
expect(() =>
238-
createElasticsearchConfig(config.schema.validate(value))
250+
expect(
251+
() => new ElasticsearchConfig(config.schema.validate(value))
239252
).toThrowErrorMatchingInlineSnapshot(
240253
`"ENOENT: no such file or directory, open '/invalid/cert'"`
241254
);
242255
});
243256

244257
it('throws if certificateAuthorities is invalid', () => {
245258
const value = { ssl: { certificateAuthorities: '/invalid/ca' } };
246-
expect(() =>
247-
createElasticsearchConfig(config.schema.validate(value))
259+
expect(
260+
() => new ElasticsearchConfig(config.schema.validate(value))
248261
).toThrowErrorMatchingInlineSnapshot(`"ENOENT: no such file or directory, open '/invalid/ca'"`);
249262
});
250263

251264
it('throws if keystore path is invalid', () => {
252265
const value = { ssl: { keystore: { path: '/invalid/keystore' } } };
253-
expect(() =>
254-
createElasticsearchConfig(config.schema.validate(value))
266+
expect(
267+
() => new ElasticsearchConfig(config.schema.validate(value))
255268
).toThrowErrorMatchingInlineSnapshot(
256269
`"ENOENT: no such file or directory, open '/invalid/keystore'"`
257270
);
258271
});
259272

260-
it('throws if keystore does not contain a key or certificate', () => {
273+
it('throws if keystore does not contain a key', () => {
261274
mockReadPkcs12Keystore.mockReturnValueOnce({});
262275
const value = { ssl: { keystore: { path: 'some-path' } } };
263-
expect(() =>
264-
createElasticsearchConfig(config.schema.validate(value))
265-
).toThrowErrorMatchingInlineSnapshot(
266-
`"Did not find key or certificate in Elasticsearch keystore."`
267-
);
276+
expect(
277+
() => new ElasticsearchConfig(config.schema.validate(value))
278+
).toThrowErrorMatchingInlineSnapshot(`"Did not find key in Elasticsearch keystore."`);
279+
});
280+
281+
it('throws if keystore does not contain a certificate', () => {
282+
mockReadPkcs12Keystore.mockReturnValueOnce({ key: 'foo' });
283+
const value = { ssl: { keystore: { path: 'some-path' } } };
284+
expect(
285+
() => new ElasticsearchConfig(config.schema.validate(value))
286+
).toThrowErrorMatchingInlineSnapshot(`"Did not find certificate in Elasticsearch keystore."`);
268287
});
269288

270289
it('throws if truststore path is invalid', () => {
271290
const value = { ssl: { keystore: { path: '/invalid/truststore' } } };
272-
expect(() =>
273-
createElasticsearchConfig(config.schema.validate(value))
291+
expect(
292+
() => new ElasticsearchConfig(config.schema.validate(value))
274293
).toThrowErrorMatchingInlineSnapshot(
275294
`"ENOENT: no such file or directory, open '/invalid/truststore'"`
276295
);
@@ -291,31 +310,47 @@ describe('throws when config is invalid', () => {
291310
});
292311
});
293312

294-
describe('logs warnings', () => {
295-
let logger: ReturnType<typeof loggingServiceMock.create>;
296-
let log: Logger;
313+
describe('deprecations', () => {
314+
it('logs a warning if elasticsearch.username is set to "elastic"', () => {
315+
const { messages } = applyElasticsearchDeprecations({ username: 'elastic' });
316+
expect(messages).toMatchInlineSnapshot(`
317+
Array [
318+
"Setting [${CONFIG_PATH}.username] to \\"elastic\\" is deprecated. You should use the \\"kibana\\" user instead.",
319+
]
320+
`);
321+
});
297322

298-
beforeAll(() => {
299-
mockReadFileSync.mockResolvedValue('foo');
323+
it('does not log a warning if elasticsearch.username is set to something besides "elastic"', () => {
324+
const { messages } = applyElasticsearchDeprecations({ username: 'otheruser' });
325+
expect(messages).toHaveLength(0);
300326
});
301327

302-
beforeEach(() => {
303-
logger = loggingServiceMock.create();
304-
log = logger.get('config');
328+
it('does not log a warning if elasticsearch.username is unset', () => {
329+
const { messages } = applyElasticsearchDeprecations({});
330+
expect(messages).toHaveLength(0);
305331
});
306332

307-
it('warns if ssl.key is set and ssl.certificate is not', () => {
308-
createElasticsearchConfig(config.schema.validate({ ssl: { key: 'some-path' } }), log);
309-
expect(loggingServiceMock.collect(logger).warn[0][0]).toMatchInlineSnapshot(
310-
`"Detected a key without a certificate; mutual TLS authentication is disabled."`
311-
);
333+
it('logs a warning if ssl.key is set and ssl.certificate is not', () => {
334+
const { messages } = applyElasticsearchDeprecations({ ssl: { key: '' } });
335+
expect(messages).toMatchInlineSnapshot(`
336+
Array [
337+
"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.",
338+
]
339+
`);
312340
});
313341

314-
it('warns if ssl.certificate is set and ssl.key is not', () => {
315-
createElasticsearchConfig(config.schema.validate({ ssl: { certificate: 'some-path' } }), log);
316-
expect(loggingServiceMock.collect(logger).warn[0][0]).toMatchInlineSnapshot(
317-
`"Detected a certificate without a key; mutual TLS authentication is disabled."`
318-
);
342+
it('logs a warning if ssl.certificate is set and ssl.key is not', () => {
343+
const { messages } = applyElasticsearchDeprecations({ ssl: { certificate: '' } });
344+
expect(messages).toMatchInlineSnapshot(`
345+
Array [
346+
"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.",
347+
]
348+
`);
349+
});
350+
351+
it('does not log a warning if both ssl.key and ssl.certificate are set', () => {
352+
const { messages } = applyElasticsearchDeprecations({ ssl: { key: '', certificate: '' } });
353+
expect(messages).toEqual([]);
319354
});
320355
});
321356

0 commit comments

Comments
 (0)