[cli] Add bin/kibana-encryption-keys (#82838)
Co-authored-by: Aleh Zasypkin <aleh.zasypkin@gmail.com> Co-authored-by: Tyler Smalley <tylersmalley@me.com>
This commit is contained in:
parent
5231a27bb6
commit
47d788d946
20
scripts/kibana_encryption_keys.js
Normal file
20
scripts/kibana_encryption_keys.js
Normal file
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
require('../src/cli_encryption_keys/dev');
|
23
src/cli_encryption_keys/__snapshots__/interactive.test.js.snap
generated
Normal file
23
src/cli_encryption_keys/__snapshots__/interactive.test.js.snap
generated
Normal file
|
@ -0,0 +1,23 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`encryption key generation interactive should write to disk partial keys 1`] = `
|
||||
Array [
|
||||
Array [
|
||||
"/foo/bar",
|
||||
"#xpack.encryptedSavedObjects.encryptionKey
|
||||
#Used to encrypt stored objects such as dashboards and visualizations
|
||||
#https://www.elastic.co/guide/en/kibana/current/xpack-security-secure-saved-objects.html#xpack-security-secure-saved-objects
|
||||
|
||||
#xpack.reporting.encryptionKey
|
||||
#Used to encrypt saved reports
|
||||
#https://www.elastic.co/guide/en/kibana/current/reporting-settings-kb.html#general-reporting-settings
|
||||
|
||||
#xpack.security.encryptionKey
|
||||
#Used to encrypt session information
|
||||
#https://www.elastic.co/guide/en/kibana/current/security-settings-kb.html#security-session-and-cookie-settings
|
||||
|
||||
xpack.encryptedSavedObjects.encryptionKey: random-key
|
||||
",
|
||||
],
|
||||
]
|
||||
`;
|
56
src/cli_encryption_keys/cli_encryption_keys.js
Normal file
56
src/cli_encryption_keys/cli_encryption_keys.js
Normal file
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import { pkg } from '../core/server/utils';
|
||||
import Command from '../cli/command';
|
||||
import { EncryptionConfig } from './encryption_config';
|
||||
|
||||
import { generateCli } from './generate';
|
||||
|
||||
const argv = process.env.kbnWorkerArgv
|
||||
? JSON.parse(process.env.kbnWorkerArgv)
|
||||
: process.argv.slice();
|
||||
const program = new Command('bin/kibana-encryption-keys');
|
||||
|
||||
program.version(pkg.version).description('A tool for managing encryption keys');
|
||||
|
||||
const encryptionConfig = new EncryptionConfig();
|
||||
|
||||
generateCli(program, encryptionConfig);
|
||||
|
||||
program
|
||||
.command('help <command>')
|
||||
.description('Get the help for a specific command')
|
||||
.action(function (cmdName) {
|
||||
const cmd = Object.values(program.commands).find((command) => command._name === cmdName);
|
||||
if (!cmd) return program.error(`unknown command ${cmdName}`);
|
||||
cmd.help();
|
||||
});
|
||||
|
||||
program.command('*', null, { noHelp: true }).action(function (cmd) {
|
||||
program.error(`unknown command ${cmd}`);
|
||||
});
|
||||
|
||||
// check for no command name
|
||||
const subCommand = argv[2] && !String(argv[2][0]).match(/^-|^\.|\//);
|
||||
if (!subCommand) {
|
||||
program.defaultHelp();
|
||||
}
|
||||
|
||||
program.parse(process.argv);
|
21
src/cli_encryption_keys/dev.js
Normal file
21
src/cli_encryption_keys/dev.js
Normal file
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
require('../setup_node_env');
|
||||
require('./cli_encryption_keys');
|
21
src/cli_encryption_keys/dist.js
Normal file
21
src/cli_encryption_keys/dist.js
Normal file
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
require('../setup_node_env/dist');
|
||||
require('./cli_encryption_keys');
|
86
src/cli_encryption_keys/encryption_config.js
Normal file
86
src/cli_encryption_keys/encryption_config.js
Normal file
|
@ -0,0 +1,86 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import crypto from 'crypto';
|
||||
import { join } from 'path';
|
||||
import { get } from 'lodash';
|
||||
import { readFileSync } from 'fs';
|
||||
import { safeLoad } from 'js-yaml';
|
||||
|
||||
import { getConfigDirectory } from '@kbn/utils';
|
||||
|
||||
export class EncryptionConfig {
|
||||
#config = safeLoad(readFileSync(join(getConfigDirectory(), 'kibana.yml')));
|
||||
#encryptionKeyPaths = [
|
||||
'xpack.encryptedSavedObjects.encryptionKey',
|
||||
'xpack.reporting.encryptionKey',
|
||||
'xpack.security.encryptionKey',
|
||||
];
|
||||
#encryptionMeta = {
|
||||
'xpack.encryptedSavedObjects.encryptionKey': {
|
||||
docs:
|
||||
'https://www.elastic.co/guide/en/kibana/current/xpack-security-secure-saved-objects.html#xpack-security-secure-saved-objects',
|
||||
description: 'Used to encrypt stored objects such as dashboards and visualizations',
|
||||
},
|
||||
'xpack.reporting.encryptionKey': {
|
||||
docs:
|
||||
'https://www.elastic.co/guide/en/kibana/current/reporting-settings-kb.html#general-reporting-settings',
|
||||
description: 'Used to encrypt saved reports',
|
||||
},
|
||||
'xpack.security.encryptionKey': {
|
||||
docs:
|
||||
'https://www.elastic.co/guide/en/kibana/current/security-settings-kb.html#security-session-and-cookie-settings',
|
||||
description: 'Used to encrypt session information',
|
||||
},
|
||||
};
|
||||
|
||||
_getEncryptionKey(key) {
|
||||
return get(this.#config, key);
|
||||
}
|
||||
|
||||
_hasEncryptionKey(key) {
|
||||
return !!get(this.#config, key);
|
||||
}
|
||||
|
||||
_generateEncryptionKey() {
|
||||
return crypto.randomBytes(16).toString('hex');
|
||||
}
|
||||
|
||||
docs({ comment } = {}) {
|
||||
const commentString = comment ? '#' : '';
|
||||
let docs = '';
|
||||
this.#encryptionKeyPaths.forEach((key) => {
|
||||
docs += `${commentString}${key}
|
||||
${commentString}${this.#encryptionMeta[key].description}
|
||||
${commentString}${this.#encryptionMeta[key].docs}
|
||||
\n`;
|
||||
});
|
||||
return docs;
|
||||
}
|
||||
|
||||
generate({ force = false }) {
|
||||
const output = {};
|
||||
this.#encryptionKeyPaths.forEach((key) => {
|
||||
if (force || !this._hasEncryptionKey(key)) {
|
||||
output[key] = this._generateEncryptionKey();
|
||||
}
|
||||
});
|
||||
return output;
|
||||
}
|
||||
}
|
83
src/cli_encryption_keys/encryption_config.test.js
Normal file
83
src/cli_encryption_keys/encryption_config.test.js
Normal file
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import { EncryptionConfig } from './encryption_config';
|
||||
import crypto from 'crypto';
|
||||
import fs from 'fs';
|
||||
|
||||
describe('encryption key configuration', () => {
|
||||
let encryptionConfig = null;
|
||||
|
||||
beforeEach(() => {
|
||||
jest.spyOn(fs, 'readFileSync').mockReturnValue('xpack.security.encryptionKey: foo');
|
||||
jest.spyOn(crypto, 'randomBytes').mockReturnValue('random-key');
|
||||
encryptionConfig = new EncryptionConfig();
|
||||
});
|
||||
it('should be able to check for encryption keys', () => {
|
||||
expect(encryptionConfig._hasEncryptionKey('xpack.reporting.encryptionKey')).toEqual(false);
|
||||
expect(encryptionConfig._hasEncryptionKey('xpack.security.encryptionKey')).toEqual(true);
|
||||
});
|
||||
|
||||
it('should be able to get encryption keys', () => {
|
||||
expect(encryptionConfig._getEncryptionKey('xpack.reporting.encryptionKey')).toBeUndefined();
|
||||
expect(encryptionConfig._getEncryptionKey('xpack.security.encryptionKey')).toEqual('foo');
|
||||
});
|
||||
|
||||
it('should generate a key', () => {
|
||||
expect(encryptionConfig._generateEncryptionKey()).toEqual('random-key');
|
||||
});
|
||||
|
||||
it('should only generate unset keys', () => {
|
||||
const output = encryptionConfig.generate({ force: false });
|
||||
expect(output['xpack.security.encryptionKey']).toEqual(undefined);
|
||||
expect(output['xpack.reporting.encryptionKey']).toEqual('random-key');
|
||||
});
|
||||
|
||||
it('should regenerate all keys if the force flag is set', () => {
|
||||
const output = encryptionConfig.generate({ force: true });
|
||||
expect(output['xpack.security.encryptionKey']).toEqual('random-key');
|
||||
expect(output['xpack.reporting.encryptionKey']).toEqual('random-key');
|
||||
expect(output['xpack.encryptedSavedObjects.encryptionKey']).toEqual('random-key');
|
||||
});
|
||||
|
||||
it('should set encryptedObjects and reporting with a default configuration', () => {
|
||||
const output = encryptionConfig.generate({});
|
||||
expect(output['xpack.security.encryptionKey']).toBeUndefined();
|
||||
expect(output['xpack.encryptedSavedObjects.encryptionKey']).toEqual('random-key');
|
||||
expect(output['xpack.reporting.encryptionKey']).toEqual('random-key');
|
||||
});
|
||||
});
|
59
src/cli_encryption_keys/generate.js
Normal file
59
src/cli_encryption_keys/generate.js
Normal file
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import { safeDump } from 'js-yaml';
|
||||
import { isEmpty } from 'lodash';
|
||||
import { interactive } from './interactive';
|
||||
import { Logger } from '../cli_plugin/lib/logger';
|
||||
|
||||
export async function generate(encryptionConfig, command) {
|
||||
const logger = new Logger();
|
||||
const keys = encryptionConfig.generate({ force: command.force });
|
||||
if (isEmpty(keys)) {
|
||||
logger.log('No keys to write. Use the --force flag to generate new keys.');
|
||||
} else {
|
||||
if (!command.quiet) {
|
||||
logger.log('## Kibana Encryption Key Generation Utility\n');
|
||||
logger.log(
|
||||
`The 'generate' command guides you through the process of setting encryption keys for:\n`
|
||||
);
|
||||
logger.log(encryptionConfig.docs());
|
||||
logger.log(
|
||||
'Already defined settings are ignored and can be regenerated using the --force flag. Check the documentation links for instructions on how to rotate encryption keys.'
|
||||
);
|
||||
logger.log('Definitions should be set in the kibana.yml used configure Kibana.\n');
|
||||
}
|
||||
if (command.interactive) {
|
||||
await interactive(keys, encryptionConfig.docs({ comment: true }), logger);
|
||||
} else {
|
||||
if (!command.quiet) logger.log('Settings:');
|
||||
logger.log(safeDump(keys));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function generateCli(program, encryptionConfig) {
|
||||
program
|
||||
.command('generate')
|
||||
.description('Generates encryption keys')
|
||||
.option('-i, --interactive', 'interactive output')
|
||||
.option('-q, --quiet', 'do not include instructions')
|
||||
.option('-f, --force', 'generate new keys for all settings')
|
||||
.action(generate.bind(null, encryptionConfig));
|
||||
}
|
56
src/cli_encryption_keys/generate.test.js
Normal file
56
src/cli_encryption_keys/generate.test.js
Normal file
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import { EncryptionConfig } from './encryption_config';
|
||||
import { generate } from './generate';
|
||||
|
||||
import { Logger } from '../cli_plugin/lib/logger';
|
||||
|
||||
describe('encryption key generation', () => {
|
||||
const encryptionConfig = new EncryptionConfig();
|
||||
beforeEach(() => {
|
||||
Logger.prototype.log = jest.fn();
|
||||
});
|
||||
|
||||
it('should generate a new encryption config', () => {
|
||||
const command = {
|
||||
force: false,
|
||||
interactive: false,
|
||||
quiet: false,
|
||||
};
|
||||
generate(encryptionConfig, command);
|
||||
const keys = Logger.prototype.log.mock.calls[6][0];
|
||||
expect(keys.search('xpack.encryptedSavedObjects.encryptionKey')).toBeGreaterThanOrEqual(0);
|
||||
expect(keys.search('xpack.reporting.encryptionKey')).toBeGreaterThanOrEqual(0);
|
||||
expect(keys.search('xpack.security.encryptionKey')).toBeGreaterThanOrEqual(0);
|
||||
expect(keys.search('foo.bar')).toEqual(-1);
|
||||
});
|
||||
|
||||
it('should only output keys if the quiet flag is set', () => {
|
||||
generate(encryptionConfig, { quiet: true });
|
||||
const keys = Logger.prototype.log.mock.calls[0][0];
|
||||
const nextLog = Logger.prototype.log.mock.calls[1];
|
||||
expect(keys.search('xpack.encryptedSavedObjects.encryptionKey')).toBeGreaterThanOrEqual(0);
|
||||
expect(nextLog).toEqual(undefined);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.restoreAllMocks();
|
||||
});
|
||||
});
|
55
src/cli_encryption_keys/interactive.js
Normal file
55
src/cli_encryption_keys/interactive.js
Normal file
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import { writeFileSync } from 'fs';
|
||||
import { join } from 'path';
|
||||
import { confirm, question } from '../cli_keystore/utils';
|
||||
import { getConfigDirectory } from '@kbn/utils';
|
||||
import { safeDump } from 'js-yaml';
|
||||
|
||||
export async function interactive(keys, docs, logger) {
|
||||
const settings = Object.keys(keys);
|
||||
logger.log(
|
||||
'This tool will ask you a number of questions in order to generate the right set of keys for your needs.\n'
|
||||
);
|
||||
const setKeys = {};
|
||||
for (const setting of settings) {
|
||||
const include = await confirm(`Set ${setting}?`);
|
||||
if (include) setKeys[setting] = keys[setting];
|
||||
}
|
||||
const count = Object.keys(setKeys).length;
|
||||
const plural = count > 1 ? 's were' : ' was';
|
||||
logger.log('');
|
||||
if (!count) return logger.log('No keys were generated');
|
||||
logger.log(`The following key${plural} generated:`);
|
||||
logger.log(Object.keys(setKeys).join('\n'));
|
||||
logger.log('');
|
||||
const write = await confirm('Save generated keys to a sample Kibana configuration file?');
|
||||
if (write) {
|
||||
const defaultSaveLocation = join(getConfigDirectory(), 'kibana.sample.yml');
|
||||
const promptedSaveLocation = await question(
|
||||
`What filename should be used for the sample Kibana config file? [${defaultSaveLocation}])`
|
||||
);
|
||||
const saveLocation = promptedSaveLocation || defaultSaveLocation;
|
||||
writeFileSync(saveLocation, docs + safeDump(setKeys));
|
||||
logger.log(`Wrote configuration to ${saveLocation}`);
|
||||
} else {
|
||||
logger.log('\nSettings:');
|
||||
logger.log(safeDump(setKeys));
|
||||
}
|
||||
}
|
69
src/cli_encryption_keys/interactive.test.js
Normal file
69
src/cli_encryption_keys/interactive.test.js
Normal file
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import { EncryptionConfig } from './encryption_config';
|
||||
import { generate } from './generate';
|
||||
|
||||
import { Logger } from '../cli_plugin/lib/logger';
|
||||
import * as prompt from '../cli_keystore/utils/prompt';
|
||||
import fs from 'fs';
|
||||
import crypto from 'crypto';
|
||||
|
||||
describe('encryption key generation interactive', () => {
|
||||
const encryptionConfig = new EncryptionConfig();
|
||||
beforeEach(() => {
|
||||
Logger.prototype.log = jest.fn();
|
||||
});
|
||||
|
||||
it('should prompt the user to write keys if the interactive flag is set', async () => {
|
||||
jest
|
||||
.spyOn(prompt, 'confirm')
|
||||
.mockResolvedValueOnce(true)
|
||||
.mockResolvedValueOnce(true)
|
||||
.mockResolvedValueOnce(true)
|
||||
.mockResolvedValueOnce(false);
|
||||
jest.spyOn(prompt, 'question');
|
||||
|
||||
await generate(encryptionConfig, { interactive: true });
|
||||
expect(prompt.confirm.mock.calls).toEqual([
|
||||
['Set xpack.encryptedSavedObjects.encryptionKey?'],
|
||||
['Set xpack.reporting.encryptionKey?'],
|
||||
['Set xpack.security.encryptionKey?'],
|
||||
['Save generated keys to a sample Kibana configuration file?'],
|
||||
]);
|
||||
expect(prompt.question).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should write to disk partial keys', async () => {
|
||||
jest
|
||||
.spyOn(prompt, 'confirm')
|
||||
.mockResolvedValueOnce(true)
|
||||
.mockResolvedValueOnce(false)
|
||||
.mockResolvedValueOnce(false)
|
||||
.mockResolvedValueOnce(true);
|
||||
jest.spyOn(prompt, 'question').mockResolvedValue('/foo/bar');
|
||||
jest.spyOn(crypto, 'randomBytes').mockReturnValue('random-key');
|
||||
fs.writeFileSync = jest.fn();
|
||||
await generate(encryptionConfig, { interactive: true });
|
||||
expect(fs.writeFileSync.mock.calls).toMatchSnapshot();
|
||||
});
|
||||
afterEach(() => {
|
||||
jest.restoreAllMocks();
|
||||
});
|
||||
});
|
29
src/dev/build/tasks/bin/scripts/kibana-encryption-keys
Executable file
29
src/dev/build/tasks/bin/scripts/kibana-encryption-keys
Executable file
|
@ -0,0 +1,29 @@
|
|||
#!/bin/sh
|
||||
SCRIPT=$0
|
||||
|
||||
# SCRIPT may be an arbitrarily deep series of symlinks. Loop until we have the concrete path.
|
||||
while [ -h "$SCRIPT" ] ; do
|
||||
ls=$(ls -ld "$SCRIPT")
|
||||
# Drop everything prior to ->
|
||||
link=$(expr "$ls" : '.*-> \(.*\)$')
|
||||
if expr "$link" : '/.*' > /dev/null; then
|
||||
SCRIPT="$link"
|
||||
else
|
||||
SCRIPT=$(dirname "$SCRIPT")/"$link"
|
||||
fi
|
||||
done
|
||||
|
||||
DIR="$(dirname "${SCRIPT}")/.."
|
||||
CONFIG_DIR=${KBN_PATH_CONF:-"$DIR/config"}
|
||||
NODE="${DIR}/node/bin/node"
|
||||
test -x "$NODE"
|
||||
if [ ! -x "$NODE" ]; then
|
||||
echo "unable to find usable node.js executable."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -f "${CONFIG_DIR}/node.options" ]; then
|
||||
KBN_NODE_OPTS="$(grep -v ^# < ${CONFIG_DIR}/node.options | xargs)"
|
||||
fi
|
||||
|
||||
NODE_OPTIONS="$KBN_NODE_OPTS $NODE_OPTIONS" "${NODE}" "${DIR}/src/cli_encryption_keys/dist" "$@"
|
|
@ -27,6 +27,7 @@ export default {
|
|||
'<rootDir>/src/legacy/server',
|
||||
'<rootDir>/src/cli',
|
||||
'<rootDir>/src/cli_keystore',
|
||||
'<rootDir>/src/cli_encryption_keys',
|
||||
'<rootDir>/src/cli_plugin',
|
||||
'<rootDir>/packages/kbn-test/target/functional_test_runner',
|
||||
'<rootDir>/src/dev',
|
||||
|
|
|
@ -172,7 +172,7 @@ describe('execute()', () => {
|
|||
apiKey: null,
|
||||
})
|
||||
).rejects.toThrowErrorMatchingInlineSnapshot(
|
||||
`"Unable to execute action due to the Encrypted Saved Objects plugin using an ephemeral encryption key. Please set xpack.encryptedSavedObjects.encryptionKey in kibana.yml"`
|
||||
`"Unable to execute action because the Encrypted Saved Objects plugin uses an ephemeral encryption key. Please set xpack.encryptedSavedObjects.encryptionKey in the kibana.yml or use the bin/kibana-encryption-keys command."`
|
||||
);
|
||||
});
|
||||
|
||||
|
|
|
@ -41,7 +41,7 @@ export function createExecutionEnqueuerFunction({
|
|||
) {
|
||||
if (isESOUsingEphemeralEncryptionKey === true) {
|
||||
throw new Error(
|
||||
`Unable to execute action due to the Encrypted Saved Objects plugin using an ephemeral encryption key. Please set xpack.encryptedSavedObjects.encryptionKey in kibana.yml`
|
||||
`Unable to execute action because the Encrypted Saved Objects plugin uses an ephemeral encryption key. Please set xpack.encryptedSavedObjects.encryptionKey in the kibana.yml or use the bin/kibana-encryption-keys command.`
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -322,7 +322,7 @@ test('throws an error when passing isESOUsingEphemeralEncryptionKey with value o
|
|||
await expect(
|
||||
customActionExecutor.execute(executeParams)
|
||||
).rejects.toThrowErrorMatchingInlineSnapshot(
|
||||
`"Unable to execute action due to the Encrypted Saved Objects plugin using an ephemeral encryption key. Please set xpack.encryptedSavedObjects.encryptionKey in kibana.yml"`
|
||||
`"Unable to execute action because the Encrypted Saved Objects plugin uses an ephemeral encryption key. Please set xpack.encryptedSavedObjects.encryptionKey in the kibana.yml or use the bin/kibana-encryption-keys command."`
|
||||
);
|
||||
});
|
||||
|
||||
|
|
|
@ -74,7 +74,7 @@ export class ActionExecutor {
|
|||
|
||||
if (this.isESOUsingEphemeralEncryptionKey === true) {
|
||||
throw new Error(
|
||||
`Unable to execute action due to the Encrypted Saved Objects plugin using an ephemeral encryption key. Please set xpack.encryptedSavedObjects.encryptionKey in kibana.yml`
|
||||
`Unable to execute action because the Encrypted Saved Objects plugin uses an ephemeral encryption key. Please set xpack.encryptedSavedObjects.encryptionKey in the kibana.yml or use the bin/kibana-encryption-keys command.`
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -56,7 +56,7 @@ describe('Actions Plugin', () => {
|
|||
await plugin.setup(coreSetup as any, pluginsSetup);
|
||||
expect(pluginsSetup.encryptedSavedObjects.usingEphemeralEncryptionKey).toEqual(true);
|
||||
expect(context.logger.get().warn).toHaveBeenCalledWith(
|
||||
'APIs are disabled due to the Encrypted Saved Objects plugin using an ephemeral encryption key. Please set xpack.encryptedSavedObjects.encryptionKey in kibana.yml.'
|
||||
'APIs are disabled because the Encrypted Saved Objects plugin uses an ephemeral encryption key. Please set xpack.encryptedSavedObjects.encryptionKey in the kibana.yml or use the bin/kibana-encryption-keys command.'
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -116,7 +116,7 @@ describe('Actions Plugin', () => {
|
|||
httpServerMock.createResponseFactory()
|
||||
)) as unknown) as RequestHandlerContext['actions'];
|
||||
expect(() => actionsContextHandler!.getActionsClient()).toThrowErrorMatchingInlineSnapshot(
|
||||
`"Unable to create actions client due to the Encrypted Saved Objects plugin using an ephemeral encryption key. Please set xpack.encryptedSavedObjects.encryptionKey in kibana.yml"`
|
||||
`"Unable to create actions client because the Encrypted Saved Objects plugin uses an ephemeral encryption key. Please set xpack.encryptedSavedObjects.encryptionKey in the kibana.yml or use the bin/kibana-encryption-keys command."`
|
||||
);
|
||||
});
|
||||
});
|
||||
|
@ -252,7 +252,7 @@ describe('Actions Plugin', () => {
|
|||
await expect(
|
||||
pluginStart.getActionsClientWithRequest(httpServerMock.createKibanaRequest())
|
||||
).rejects.toThrowErrorMatchingInlineSnapshot(
|
||||
`"Unable to create actions client due to the Encrypted Saved Objects plugin using an ephemeral encryption key. Please set xpack.encryptedSavedObjects.encryptionKey in kibana.yml"`
|
||||
`"Unable to create actions client because the Encrypted Saved Objects plugin uses an ephemeral encryption key. Please set xpack.encryptedSavedObjects.encryptionKey in the kibana.yml or use the bin/kibana-encryption-keys command."`
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -171,7 +171,7 @@ export class ActionsPlugin implements Plugin<Promise<PluginSetupContract>, Plugi
|
|||
|
||||
if (this.isESOUsingEphemeralEncryptionKey) {
|
||||
this.logger.warn(
|
||||
'APIs are disabled due to the Encrypted Saved Objects plugin using an ephemeral encryption key. Please set xpack.encryptedSavedObjects.encryptionKey in kibana.yml.'
|
||||
'APIs are disabled because the Encrypted Saved Objects plugin uses an ephemeral encryption key. Please set xpack.encryptedSavedObjects.encryptionKey in the kibana.yml or use the bin/kibana-encryption-keys command.'
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -292,7 +292,7 @@ export class ActionsPlugin implements Plugin<Promise<PluginSetupContract>, Plugi
|
|||
) => {
|
||||
if (isESOUsingEphemeralEncryptionKey === true) {
|
||||
throw new Error(
|
||||
`Unable to create actions client due to the Encrypted Saved Objects plugin using an ephemeral encryption key. Please set xpack.encryptedSavedObjects.encryptionKey in kibana.yml`
|
||||
`Unable to create actions client because the Encrypted Saved Objects plugin uses an ephemeral encryption key. Please set xpack.encryptedSavedObjects.encryptionKey in the kibana.yml or use the bin/kibana-encryption-keys command.`
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -446,7 +446,7 @@ export class ActionsPlugin implements Plugin<Promise<PluginSetupContract>, Plugi
|
|||
getActionsClient: () => {
|
||||
if (isESOUsingEphemeralEncryptionKey === true) {
|
||||
throw new Error(
|
||||
`Unable to create actions client due to the Encrypted Saved Objects plugin using an ephemeral encryption key. Please set xpack.encryptedSavedObjects.encryptionKey in kibana.yml`
|
||||
`Unable to create actions client because the Encrypted Saved Objects plugin uses an ephemeral encryption key. Please set xpack.encryptedSavedObjects.encryptionKey in the kibana.yml or use the bin/kibana-encryption-keys command.`
|
||||
);
|
||||
}
|
||||
return new ActionsClient({
|
||||
|
|
|
@ -52,7 +52,7 @@ describe('Alerting Plugin', () => {
|
|||
expect(statusMock.set).toHaveBeenCalledTimes(1);
|
||||
expect(encryptedSavedObjectsSetup.usingEphemeralEncryptionKey).toEqual(true);
|
||||
expect(context.logger.get().warn).toHaveBeenCalledWith(
|
||||
'APIs are disabled due to the Encrypted Saved Objects plugin using an ephemeral encryption key. Please set xpack.encryptedSavedObjects.encryptionKey in kibana.yml.'
|
||||
'APIs are disabled because the Encrypted Saved Objects plugin uses an ephemeral encryption key. Please set xpack.encryptedSavedObjects.encryptionKey in the kibana.yml or use the bin/kibana-encryption-keys command.'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
@ -113,7 +113,7 @@ describe('Alerting Plugin', () => {
|
|||
expect(() =>
|
||||
startContract.getAlertsClientWithRequest({} as KibanaRequest)
|
||||
).toThrowErrorMatchingInlineSnapshot(
|
||||
`"Unable to create alerts client due to the Encrypted Saved Objects plugin using an ephemeral encryption key. Please set xpack.encryptedSavedObjects.encryptionKey in kibana.yml"`
|
||||
`"Unable to create alerts client because the Encrypted Saved Objects plugin uses an ephemeral encryption key. Please set xpack.encryptedSavedObjects.encryptionKey in the kibana.yml or use the bin/kibana-encryption-keys command."`
|
||||
);
|
||||
});
|
||||
|
||||
|
|
|
@ -169,7 +169,7 @@ export class AlertingPlugin {
|
|||
|
||||
if (this.isESOUsingEphemeralEncryptionKey) {
|
||||
this.logger.warn(
|
||||
'APIs are disabled due to the Encrypted Saved Objects plugin using an ephemeral encryption key. Please set xpack.encryptedSavedObjects.encryptionKey in kibana.yml.'
|
||||
'APIs are disabled because the Encrypted Saved Objects plugin uses an ephemeral encryption key. Please set xpack.encryptedSavedObjects.encryptionKey in the kibana.yml or use the bin/kibana-encryption-keys command.'
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -296,7 +296,7 @@ export class AlertingPlugin {
|
|||
const getAlertsClientWithRequest = (request: KibanaRequest) => {
|
||||
if (isESOUsingEphemeralEncryptionKey === true) {
|
||||
throw new Error(
|
||||
`Unable to create alerts client due to the Encrypted Saved Objects plugin using an ephemeral encryption key. Please set xpack.encryptedSavedObjects.encryptionKey in kibana.yml`
|
||||
`Unable to create alerts client because the Encrypted Saved Objects plugin uses an ephemeral encryption key. Please set xpack.encryptedSavedObjects.encryptionKey in the kibana.yml or use the bin/kibana-encryption-keys command.`
|
||||
);
|
||||
}
|
||||
return alertsClientFactory!.create(request, core.savedObjects);
|
||||
|
|
|
@ -138,7 +138,7 @@ describe('createConfig()', () => {
|
|||
expect(loggingSystemMock.collect(logger).warn).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
Array [
|
||||
"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",
|
||||
"Generating a random key for xpack.encryptedSavedObjects.encryptionKey. To decrypt encrypted saved objects attributes after restart, please set xpack.encryptedSavedObjects.encryptionKey in the kibana.yml or use the bin/kibana-encryption-keys command.",
|
||||
],
|
||||
]
|
||||
`);
|
||||
|
|
|
@ -39,8 +39,8 @@ export function createConfig(config: TypeOf<typeof ConfigSchema>, logger: Logger
|
|||
if (encryptionKey === undefined) {
|
||||
logger.warn(
|
||||
'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'
|
||||
'To decrypt encrypted saved objects attributes after restart, ' +
|
||||
'please set xpack.encryptedSavedObjects.encryptionKey in the kibana.yml or use the bin/kibana-encryption-keys command.'
|
||||
);
|
||||
|
||||
encryptionKey = crypto.randomBytes(16).toString('hex');
|
||||
|
|
|
@ -235,7 +235,7 @@ export class FleetPlugin
|
|||
if (isESOUsingEphemeralEncryptionKey) {
|
||||
if (this.logger) {
|
||||
this.logger.warn(
|
||||
'Fleet APIs are disabled due to the Encrypted Saved Objects plugin using an ephemeral encryption key. Please set xpack.encryptedSavedObjects.encryptionKey in kibana.yml.'
|
||||
'Fleet APIs are disabled because the Encrypted Saved Objects plugin uses an ephemeral encryption key. Please set xpack.encryptedSavedObjects.encryptionKey in the kibana.yml or use the bin/kibana-encryption-keys command.'
|
||||
);
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -70,7 +70,7 @@ describe('Reporting server createConfig$', () => {
|
|||
`);
|
||||
expect((mockLogger.warn as any).mock.calls.length).toBe(1);
|
||||
expect((mockLogger.warn as any).mock.calls[0]).toMatchObject([
|
||||
'Generating a random key for xpack.reporting.encryptionKey. To prevent sessions from being invalidated on restart, please set xpack.reporting.encryptionKey in kibana.yml',
|
||||
'Generating a random key for xpack.reporting.encryptionKey. To prevent sessions from being invalidated on restart, please set xpack.reporting.encryptionKey in the kibana.yml or use the bin/kibana-encryption-keys command.',
|
||||
]);
|
||||
});
|
||||
|
||||
|
|
|
@ -35,7 +35,7 @@ export function createConfig$(
|
|||
i18n.translate('xpack.reporting.serverConfig.randomEncryptionKey', {
|
||||
defaultMessage:
|
||||
'Generating a random key for xpack.reporting.encryptionKey. To prevent sessions from being invalidated on ' +
|
||||
'restart, please set xpack.reporting.encryptionKey in kibana.yml',
|
||||
'restart, please set xpack.reporting.encryptionKey in the kibana.yml or use the bin/kibana-encryption-keys command.',
|
||||
})
|
||||
);
|
||||
encryptionKey = crypto.randomBytes(16).toString('hex');
|
||||
|
|
|
@ -1119,12 +1119,12 @@ describe('createConfig()', () => {
|
|||
expect(config.encryptionKey).toEqual('ab'.repeat(16));
|
||||
|
||||
expect(loggingSystemMock.collect(logger).warn).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
Array [
|
||||
"Generating a random key for xpack.security.encryptionKey. To prevent sessions from being invalidated on restart, please set xpack.security.encryptionKey in kibana.yml",
|
||||
],
|
||||
]
|
||||
`);
|
||||
Array [
|
||||
Array [
|
||||
"Generating a random key for xpack.security.encryptionKey. To prevent sessions from being invalidated on restart, please set xpack.security.encryptionKey in the kibana.yml or use the bin/kibana-encryption-keys command.",
|
||||
],
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
it('should log a warning if SSL is not configured', async () => {
|
||||
|
|
|
@ -252,7 +252,7 @@ export function createConfig(
|
|||
if (encryptionKey === undefined) {
|
||||
logger.warn(
|
||||
'Generating a random key for xpack.security.encryptionKey. To prevent sessions from being invalidated on ' +
|
||||
'restart, please set xpack.security.encryptionKey in kibana.yml'
|
||||
'restart, please set xpack.security.encryptionKey in the kibana.yml or use the bin/kibana-encryption-keys command.'
|
||||
);
|
||||
|
||||
encryptionKey = crypto.randomBytes(16).toString('hex');
|
||||
|
|
Loading…
Reference in a new issue