Skip to content

Commit 35f6354

Browse files
Merge branch 'master' into implement/release-note-dev-docs
2 parents 7293849 + bd80d3c commit 35f6354

File tree

189 files changed

+7647
-887
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

189 files changed

+7647
-887
lines changed

docs/setup/production.asciidoc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,7 @@ Settings that must be the same:
135135
xpack.security.encryptionKey //decrypting session information
136136
xpack.reporting.encryptionKey //decrypting reports
137137
xpack.encryptedSavedObjects.encryptionKey // decrypting saved objects
138+
xpack.encryptedSavedObjects.keyRotation.decryptionOnlyKeys // saved objects encryption key rotation, if any
138139
--------
139140

140141
Separate configuration files can be used from the command line by using the `-c` flag:

src/dev/build/tasks/os_packages/docker_generator/resources/bin/kibana-docker

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,7 @@ kibana_vars=(
159159
xpack.code.security.gitHostWhitelist
160160
xpack.code.security.gitProtocolWhitelist
161161
xpack.encryptedSavedObjects.encryptionKey
162+
xpack.encryptedSavedObjects.keyRotation.decryptionOnlyKeys
162163
xpack.graph.enabled
163164
xpack.graph.canEditDrillDownUrls
164165
xpack.graph.savePolicy

src/plugins/home/public/application/components/app_navigation_handler.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ export const createAppNavigationHandler = (targetUrl: string) => (event: MouseEv
2424
if (event.altKey || event.metaKey || event.ctrlKey) {
2525
return;
2626
}
27+
const { application, addBasePath } = getServices();
2728
event.preventDefault();
28-
getServices().application.navigateToUrl(targetUrl);
29+
application.navigateToUrl(addBasePath(targetUrl));
2930
};

x-pack/plugins/encrypted_saved_objects/server/config.test.ts

Lines changed: 93 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,29 +6,60 @@
66

77
jest.mock('crypto', () => ({ randomBytes: jest.fn() }));
88

9-
import { first } from 'rxjs/operators';
10-
import { loggingSystemMock, coreMock } from 'src/core/server/mocks';
11-
import { createConfig$, ConfigSchema } from './config';
9+
import { loggingSystemMock } from 'src/core/server/mocks';
10+
import { createConfig, ConfigSchema } from './config';
1211

1312
describe('config schema', () => {
1413
it('generates proper defaults', () => {
1514
expect(ConfigSchema.validate({})).toMatchInlineSnapshot(`
1615
Object {
1716
"enabled": true,
1817
"encryptionKey": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
18+
"keyRotation": Object {
19+
"decryptionOnlyKeys": Array [],
20+
},
1921
}
2022
`);
2123

2224
expect(ConfigSchema.validate({}, { dist: false })).toMatchInlineSnapshot(`
2325
Object {
2426
"enabled": true,
2527
"encryptionKey": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
28+
"keyRotation": Object {
29+
"decryptionOnlyKeys": Array [],
30+
},
2631
}
2732
`);
2833

2934
expect(ConfigSchema.validate({}, { dist: true })).toMatchInlineSnapshot(`
3035
Object {
3136
"enabled": true,
37+
"keyRotation": Object {
38+
"decryptionOnlyKeys": Array [],
39+
},
40+
}
41+
`);
42+
});
43+
44+
it('properly validates config', () => {
45+
expect(
46+
ConfigSchema.validate(
47+
{
48+
encryptionKey: 'a'.repeat(32),
49+
keyRotation: { decryptionOnlyKeys: ['b'.repeat(32), 'c'.repeat(32)] },
50+
},
51+
{ dist: true }
52+
)
53+
).toMatchInlineSnapshot(`
54+
Object {
55+
"enabled": true,
56+
"encryptionKey": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
57+
"keyRotation": Object {
58+
"decryptionOnlyKeys": Array [
59+
"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
60+
"cccccccccccccccccccccccccccccccc",
61+
],
62+
},
3263
}
3364
`);
3465
});
@@ -46,21 +77,65 @@ describe('config schema', () => {
4677
`"[encryptionKey]: value has length [3] but it must have a minimum length of [32]."`
4778
);
4879
});
80+
81+
it('should throw error if any of the xpack.encryptedSavedObjects.keyRotation.decryptionOnlyKeys is less than 32 characters', () => {
82+
expect(() =>
83+
ConfigSchema.validate({
84+
keyRotation: { decryptionOnlyKeys: ['a'.repeat(32), 'b'.repeat(31)] },
85+
})
86+
).toThrowErrorMatchingInlineSnapshot(
87+
`"[keyRotation.decryptionOnlyKeys.1]: value has length [31] but it must have a minimum length of [32]."`
88+
);
89+
90+
expect(() =>
91+
ConfigSchema.validate(
92+
{ keyRotation: { decryptionOnlyKeys: ['a'.repeat(32), 'b'.repeat(31)] } },
93+
{ dist: true }
94+
)
95+
).toThrowErrorMatchingInlineSnapshot(
96+
`"[keyRotation.decryptionOnlyKeys.1]: value has length [31] but it must have a minimum length of [32]."`
97+
);
98+
});
99+
100+
it('should throw error if any of the xpack.encryptedSavedObjects.keyRotation.decryptionOnlyKeys is equal to xpack.encryptedSavedObjects.encryptionKey', () => {
101+
expect(() =>
102+
ConfigSchema.validate({
103+
encryptionKey: 'a'.repeat(32),
104+
keyRotation: { decryptionOnlyKeys: ['a'.repeat(32)] },
105+
})
106+
).toThrowErrorMatchingInlineSnapshot(
107+
`"\`keyRotation.decryptionOnlyKeys\` cannot contain primary encryption key specified in \`encryptionKey\`."`
108+
);
109+
110+
expect(() =>
111+
ConfigSchema.validate(
112+
{
113+
encryptionKey: 'a'.repeat(32),
114+
keyRotation: { decryptionOnlyKeys: ['a'.repeat(32)] },
115+
},
116+
{ dist: true }
117+
)
118+
).toThrowErrorMatchingInlineSnapshot(
119+
`"\`keyRotation.decryptionOnlyKeys\` cannot contain primary encryption key specified in \`encryptionKey\`."`
120+
);
121+
});
49122
});
50123

51-
describe('createConfig$()', () => {
52-
it('should log a warning, set xpack.encryptedSavedObjects.encryptionKey and usingEphemeralEncryptionKey=true when encryptionKey is not set', async () => {
124+
describe('createConfig()', () => {
125+
it('should log a warning, set xpack.encryptedSavedObjects.encryptionKey and usingEphemeralEncryptionKey=true when encryptionKey is not set', () => {
53126
const mockRandomBytes = jest.requireMock('crypto').randomBytes;
54127
mockRandomBytes.mockReturnValue('ab'.repeat(16));
55128

56-
const contextMock = coreMock.createPluginInitializerContext({});
57-
const config = await createConfig$(contextMock).pipe(first()).toPromise();
129+
const logger = loggingSystemMock.create().get();
130+
const config = createConfig(ConfigSchema.validate({}, { dist: true }), logger);
58131
expect(config).toEqual({
59-
config: { encryptionKey: 'ab'.repeat(16) },
132+
enabled: true,
133+
encryptionKey: 'ab'.repeat(16),
134+
keyRotation: { decryptionOnlyKeys: [] },
60135
usingEphemeralEncryptionKey: true,
61136
});
62137

63-
expect(loggingSystemMock.collect(contextMock.logger).warn).toMatchInlineSnapshot(`
138+
expect(loggingSystemMock.collect(logger).warn).toMatchInlineSnapshot(`
64139
Array [
65140
Array [
66141
"Generating a random key for xpack.encryptedSavedObjects.encryptionKey. To be able to decrypt encrypted saved objects attributes after restart, please set xpack.encryptedSavedObjects.encryptionKey in kibana.yml",
@@ -70,15 +145,18 @@ describe('createConfig$()', () => {
70145
});
71146

72147
it('should not log a warning and set usingEphemeralEncryptionKey=false when encryptionKey is set', async () => {
73-
const contextMock = coreMock.createPluginInitializerContext({
74-
encryptionKey: 'supersecret',
75-
});
76-
const config = await createConfig$(contextMock).pipe(first()).toPromise();
148+
const logger = loggingSystemMock.create().get();
149+
const config = createConfig(
150+
ConfigSchema.validate({ encryptionKey: 'supersecret'.repeat(3) }, { dist: true }),
151+
logger
152+
);
77153
expect(config).toEqual({
78-
config: { encryptionKey: 'supersecret' },
154+
enabled: true,
155+
encryptionKey: 'supersecret'.repeat(3),
156+
keyRotation: { decryptionOnlyKeys: [] },
79157
usingEphemeralEncryptionKey: false,
80158
});
81159

82-
expect(loggingSystemMock.collect(contextMock.logger).warn).toEqual([]);
160+
expect(loggingSystemMock.collect(logger).warn).toEqual([]);
83161
});
84162
});

x-pack/plugins/encrypted_saved_objects/server/config.ts

Lines changed: 40 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -5,41 +5,50 @@
55
*/
66

77
import crypto from 'crypto';
8-
import { map } from 'rxjs/operators';
98
import { schema, TypeOf } from '@kbn/config-schema';
10-
import { PluginInitializerContext } from 'src/core/server';
9+
import { Logger } from 'src/core/server';
1110

12-
export const ConfigSchema = schema.object({
13-
enabled: schema.boolean({ defaultValue: true }),
14-
encryptionKey: schema.conditional(
15-
schema.contextRef('dist'),
16-
true,
17-
schema.maybe(schema.string({ minLength: 32 })),
18-
schema.string({ minLength: 32, defaultValue: 'a'.repeat(32) })
19-
),
20-
});
11+
export type ConfigType = ReturnType<typeof createConfig>;
2112

22-
export function createConfig$(context: PluginInitializerContext) {
23-
return context.config.create<TypeOf<typeof ConfigSchema>>().pipe(
24-
map((config) => {
25-
const logger = context.logger.get('config');
13+
export const ConfigSchema = schema.object(
14+
{
15+
enabled: schema.boolean({ defaultValue: true }),
16+
encryptionKey: schema.conditional(
17+
schema.contextRef('dist'),
18+
true,
19+
schema.maybe(schema.string({ minLength: 32 })),
20+
schema.string({ minLength: 32, defaultValue: 'a'.repeat(32) })
21+
),
22+
keyRotation: schema.object({
23+
decryptionOnlyKeys: schema.arrayOf(schema.string({ minLength: 32 }), { defaultValue: [] }),
24+
}),
25+
},
26+
{
27+
validate(value) {
28+
const decryptionOnlyKeys = value.keyRotation?.decryptionOnlyKeys ?? [];
29+
if (value.encryptionKey && decryptionOnlyKeys.includes(value.encryptionKey)) {
30+
return '`keyRotation.decryptionOnlyKeys` cannot contain primary encryption key specified in `encryptionKey`.';
31+
}
32+
},
33+
}
34+
);
2635

27-
let encryptionKey = config.encryptionKey;
28-
const usingEphemeralEncryptionKey = encryptionKey === undefined;
29-
if (encryptionKey === undefined) {
30-
logger.warn(
31-
'Generating a random key for xpack.encryptedSavedObjects.encryptionKey. ' +
32-
'To be able to decrypt encrypted saved objects attributes after restart, ' +
33-
'please set xpack.encryptedSavedObjects.encryptionKey in kibana.yml'
34-
);
36+
export function createConfig(config: TypeOf<typeof ConfigSchema>, logger: Logger) {
37+
let encryptionKey = config.encryptionKey;
38+
const usingEphemeralEncryptionKey = encryptionKey === undefined;
39+
if (encryptionKey === undefined) {
40+
logger.warn(
41+
'Generating a random key for xpack.encryptedSavedObjects.encryptionKey. ' +
42+
'To be able to decrypt encrypted saved objects attributes after restart, ' +
43+
'please set xpack.encryptedSavedObjects.encryptionKey in kibana.yml'
44+
);
3545

36-
encryptionKey = crypto.randomBytes(16).toString('hex');
37-
}
46+
encryptionKey = crypto.randomBytes(16).toString('hex');
47+
}
3848

39-
return {
40-
config: { ...config, encryptionKey },
41-
usingEphemeralEncryptionKey,
42-
};
43-
})
44-
);
49+
return {
50+
...config,
51+
encryptionKey,
52+
usingEphemeralEncryptionKey,
53+
};
4554
}

0 commit comments

Comments
 (0)