mirror of
https://github.com/elastic/kibana.git
synced 2025-06-28 11:05:39 -04:00
Add interactive setup CLI (#114493)
* Add interactive setup CLI
* Added tsconfig
* ignore all CLI dev.js files when building
* add cli_init to the root TS project and setup necessary ref
* Fix type errors
* Added suggestions from code review
* ts fix
* fixed build dependencies
* Added suggestions from code review
* fix type definitions
* fix types
* upgraded commander to fix ts issues
* Revert "upgraded commander to fix ts issues"
This reverts commit 52b8943222
.
* upgraded commander
Co-authored-by: spalger <spalger@users.noreply.github.com>
Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
abd5e9ffa8
commit
b879a9a497
18 changed files with 442 additions and 30 deletions
|
@ -197,7 +197,7 @@
|
||||||
"chroma-js": "^1.4.1",
|
"chroma-js": "^1.4.1",
|
||||||
"classnames": "2.2.6",
|
"classnames": "2.2.6",
|
||||||
"color": "1.0.3",
|
"color": "1.0.3",
|
||||||
"commander": "^3.0.2",
|
"commander": "^4.1.1",
|
||||||
"compare-versions": "3.5.1",
|
"compare-versions": "3.5.1",
|
||||||
"concat-stream": "1.6.2",
|
"concat-stream": "1.6.2",
|
||||||
"constate": "^1.3.2",
|
"constate": "^1.3.2",
|
||||||
|
@ -248,6 +248,7 @@
|
||||||
"idx": "^2.5.6",
|
"idx": "^2.5.6",
|
||||||
"immer": "^9.0.6",
|
"immer": "^9.0.6",
|
||||||
"inline-style": "^2.0.0",
|
"inline-style": "^2.0.0",
|
||||||
|
"inquirer": "^7.3.3",
|
||||||
"intl": "^1.2.5",
|
"intl": "^1.2.5",
|
||||||
"intl-format-cache": "^2.1.0",
|
"intl-format-cache": "^2.1.0",
|
||||||
"intl-messageformat": "^2.2.0",
|
"intl-messageformat": "^2.2.0",
|
||||||
|
@ -297,6 +298,7 @@
|
||||||
"object-hash": "^1.3.1",
|
"object-hash": "^1.3.1",
|
||||||
"object-path-immutable": "^3.1.1",
|
"object-path-immutable": "^3.1.1",
|
||||||
"opn": "^5.5.0",
|
"opn": "^5.5.0",
|
||||||
|
"ora": "^4.0.4",
|
||||||
"p-limit": "^3.0.1",
|
"p-limit": "^3.0.1",
|
||||||
"p-map": "^4.0.0",
|
"p-map": "^4.0.0",
|
||||||
"p-retry": "^4.2.0",
|
"p-retry": "^4.2.0",
|
||||||
|
@ -720,7 +722,6 @@
|
||||||
"html": "1.0.0",
|
"html": "1.0.0",
|
||||||
"html-loader": "^0.5.5",
|
"html-loader": "^0.5.5",
|
||||||
"http-proxy": "^1.18.1",
|
"http-proxy": "^1.18.1",
|
||||||
"inquirer": "^7.3.3",
|
|
||||||
"is-glob": "^4.0.1",
|
"is-glob": "^4.0.1",
|
||||||
"is-path-inside": "^3.0.2",
|
"is-path-inside": "^3.0.2",
|
||||||
"istanbul-instrumenter-loader": "^3.0.1",
|
"istanbul-instrumenter-loader": "^3.0.1",
|
||||||
|
@ -762,7 +763,6 @@
|
||||||
"null-loader": "^3.0.0",
|
"null-loader": "^3.0.0",
|
||||||
"nyc": "^15.0.1",
|
"nyc": "^15.0.1",
|
||||||
"oboe": "^2.1.4",
|
"oboe": "^2.1.4",
|
||||||
"ora": "^4.0.4",
|
|
||||||
"parse-link-header": "^1.0.1",
|
"parse-link-header": "^1.0.1",
|
||||||
"pbf": "3.2.1",
|
"pbf": "3.2.1",
|
||||||
"pirates": "^4.0.1",
|
"pirates": "^4.0.1",
|
||||||
|
|
9
scripts/kibana_setup.js
Normal file
9
scripts/kibana_setup.js
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
/*
|
||||||
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||||
|
* or more contributor license agreements. Licensed under the Elastic License
|
||||||
|
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||||
|
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||||
|
* Side Public License, v 1.
|
||||||
|
*/
|
||||||
|
|
||||||
|
require('../src/cli_setup/dev');
|
20
src/cli_plugin/lib/logger.d.ts
vendored
Normal file
20
src/cli_plugin/lib/logger.d.ts
vendored
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
/*
|
||||||
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||||
|
* or more contributor license agreements. Licensed under the Elastic License
|
||||||
|
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||||
|
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||||
|
* Side Public License, v 1.
|
||||||
|
*/
|
||||||
|
|
||||||
|
interface LoggerOptions {
|
||||||
|
silent?: boolean;
|
||||||
|
quiet?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export declare class Logger {
|
||||||
|
constructor(settings?: LoggerOptions);
|
||||||
|
|
||||||
|
log(data: string, sameLine?: boolean): void;
|
||||||
|
|
||||||
|
error(data: string): void;
|
||||||
|
}
|
118
src/cli_setup/cli_setup.ts
Normal file
118
src/cli_setup/cli_setup.ts
Normal file
|
@ -0,0 +1,118 @@
|
||||||
|
/*
|
||||||
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||||
|
* or more contributor license agreements. Licensed under the Elastic License
|
||||||
|
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||||
|
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||||
|
* Side Public License, v 1.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { kibanaPackageJson } from '@kbn/utils';
|
||||||
|
import chalk from 'chalk';
|
||||||
|
import ora from 'ora';
|
||||||
|
import { Command } from 'commander';
|
||||||
|
import { getConfigPath } from '@kbn/utils';
|
||||||
|
|
||||||
|
import {
|
||||||
|
ElasticsearchService,
|
||||||
|
EnrollResult,
|
||||||
|
} from '../plugins/interactive_setup/server/elasticsearch_service';
|
||||||
|
import { getDetailedErrorMessage } from '../plugins/interactive_setup/server/errors';
|
||||||
|
import {
|
||||||
|
promptToken,
|
||||||
|
getCommand,
|
||||||
|
decodeEnrollmentToken,
|
||||||
|
kibanaConfigWriter,
|
||||||
|
elasticsearch,
|
||||||
|
} from './utils';
|
||||||
|
import { Logger } from '../cli_plugin/lib/logger';
|
||||||
|
|
||||||
|
const program = new Command('bin/kibana-setup');
|
||||||
|
|
||||||
|
program
|
||||||
|
.version(kibanaPackageJson.version)
|
||||||
|
.description(
|
||||||
|
'This command walks you through all required steps to securely connect Kibana with Elasticsearch'
|
||||||
|
)
|
||||||
|
.option('-t, --token <token>', 'Elasticsearch enrollment token')
|
||||||
|
.option('-s, --silent', 'Prevent all logging');
|
||||||
|
|
||||||
|
program.parse(process.argv);
|
||||||
|
|
||||||
|
interface SetupOptions {
|
||||||
|
token?: string;
|
||||||
|
silent?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const options = program.opts() as SetupOptions;
|
||||||
|
const spinner = ora();
|
||||||
|
const logger = new Logger(options);
|
||||||
|
|
||||||
|
async function initCommand() {
|
||||||
|
const token = decodeEnrollmentToken(
|
||||||
|
options.token ?? (options.silent ? undefined : await promptToken())
|
||||||
|
);
|
||||||
|
if (!token) {
|
||||||
|
logger.error(chalk.red('Invalid enrollment token provided.'));
|
||||||
|
logger.error('');
|
||||||
|
logger.error('To generate a new enrollment token run:');
|
||||||
|
logger.error(` ${getCommand('elasticsearch-create-enrollment-token', '-s kibana')}`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(await kibanaConfigWriter.isConfigWritable())) {
|
||||||
|
logger.error(chalk.red('Kibana does not have enough permissions to write to the config file.'));
|
||||||
|
logger.error('');
|
||||||
|
logger.error('To grant write access run:');
|
||||||
|
logger.error(` chmod +w ${getConfigPath()}`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.log('');
|
||||||
|
if (!options.silent) {
|
||||||
|
spinner.start(chalk.dim('Configuring Kibana...'));
|
||||||
|
}
|
||||||
|
|
||||||
|
let configToWrite: EnrollResult;
|
||||||
|
try {
|
||||||
|
configToWrite = await elasticsearch.enroll({
|
||||||
|
hosts: token.adr,
|
||||||
|
apiKey: token.key,
|
||||||
|
caFingerprint: ElasticsearchService.formatFingerprint(token.fgr),
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
if (!options.silent) {
|
||||||
|
spinner.fail(
|
||||||
|
`${chalk.bold('Unable to enroll with Elasticsearch:')} ${chalk.red(
|
||||||
|
`${getDetailedErrorMessage(error)}`
|
||||||
|
)}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
logger.error('');
|
||||||
|
logger.error('To generate a new enrollment token run:');
|
||||||
|
logger.error(` ${getCommand('elasticsearch-create-enrollment-token', '-s kibana')}`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await kibanaConfigWriter.writeConfig(configToWrite);
|
||||||
|
} catch (error) {
|
||||||
|
if (!options.silent) {
|
||||||
|
spinner.fail(
|
||||||
|
`${chalk.bold('Unable to configure Kibana:')} ${chalk.red(
|
||||||
|
`${getDetailedErrorMessage(error)}`
|
||||||
|
)}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
logger.error(chalk.red(`${getDetailedErrorMessage(error)}`));
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!options.silent) {
|
||||||
|
spinner.succeed(chalk.bold('Kibana configured successfully.'));
|
||||||
|
}
|
||||||
|
logger.log('');
|
||||||
|
logger.log('To start Kibana run:');
|
||||||
|
logger.log(` ${getCommand('kibana')}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
initCommand();
|
10
src/cli_setup/dev.js
Normal file
10
src/cli_setup/dev.js
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
/*
|
||||||
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||||
|
* or more contributor license agreements. Licensed under the Elastic License
|
||||||
|
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||||
|
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||||
|
* Side Public License, v 1.
|
||||||
|
*/
|
||||||
|
|
||||||
|
require('../setup_node_env');
|
||||||
|
require('./cli_setup');
|
10
src/cli_setup/dist.js
Normal file
10
src/cli_setup/dist.js
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
/*
|
||||||
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||||
|
* or more contributor license agreements. Licensed under the Elastic License
|
||||||
|
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||||
|
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||||
|
* Side Public License, v 1.
|
||||||
|
*/
|
||||||
|
|
||||||
|
require('../setup_node_env/dist');
|
||||||
|
require('./cli_setup');
|
13
src/cli_setup/jest.config.js
Normal file
13
src/cli_setup/jest.config.js
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
/*
|
||||||
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||||
|
* or more contributor license agreements. Licensed under the Elastic License
|
||||||
|
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||||
|
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||||
|
* Side Public License, v 1.
|
||||||
|
*/
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
preset: '@kbn/test',
|
||||||
|
rootDir: '../..',
|
||||||
|
roots: ['<rootDir>/src/cli_setup'],
|
||||||
|
};
|
76
src/cli_setup/utils.test.ts
Normal file
76
src/cli_setup/utils.test.ts
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
/*
|
||||||
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||||
|
* or more contributor license agreements. Licensed under the Elastic License
|
||||||
|
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||||
|
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||||
|
* Side Public License, v 1.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { decodeEnrollmentToken, getCommand } from './utils';
|
||||||
|
import type { EnrollmentToken } from '../plugins/interactive_setup/common';
|
||||||
|
|
||||||
|
describe('kibana setup cli', () => {
|
||||||
|
describe('getCommand', () => {
|
||||||
|
const originalPlatform = process.platform;
|
||||||
|
|
||||||
|
it('should format windows correctly', () => {
|
||||||
|
Object.defineProperty(process, 'platform', {
|
||||||
|
value: 'win32',
|
||||||
|
});
|
||||||
|
expect(getCommand('kibana')).toEqual('bin\\kibana.bat');
|
||||||
|
expect(getCommand('kibana', '--silent')).toEqual('bin\\kibana.bat --silent');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should format unix correctly', () => {
|
||||||
|
Object.defineProperty(process, 'platform', {
|
||||||
|
value: 'linux',
|
||||||
|
});
|
||||||
|
expect(getCommand('kibana')).toEqual('bin/kibana');
|
||||||
|
expect(getCommand('kibana', '--silent')).toEqual('bin/kibana --silent');
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(function () {
|
||||||
|
Object.defineProperty(process, 'platform', {
|
||||||
|
value: originalPlatform,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('decodeEnrollmentToken', () => {
|
||||||
|
const token: EnrollmentToken = {
|
||||||
|
ver: '8.0.0',
|
||||||
|
adr: ['localhost:9200'],
|
||||||
|
fgr: 'AA:C8:2C:2E:09:58:F4:FE:A1:D2:AB:7F:13:70:C2:7D:EB:FD:A2:23:88:13:E4:DA:3A:D0:59:D0:09:00:07:36',
|
||||||
|
key: 'JH-36HoBo4EYIoVhHh2F:uEo4dksARMq_BSHaAHUr8Q',
|
||||||
|
};
|
||||||
|
|
||||||
|
it('should decode a valid token', () => {
|
||||||
|
expect(decodeEnrollmentToken(btoa(JSON.stringify(token)))).toEqual({
|
||||||
|
adr: ['https://localhost:9200'],
|
||||||
|
fgr: 'AA:C8:2C:2E:09:58:F4:FE:A1:D2:AB:7F:13:70:C2:7D:EB:FD:A2:23:88:13:E4:DA:3A:D0:59:D0:09:00:07:36',
|
||||||
|
key: 'SkgtMzZIb0JvNEVZSW9WaEhoMkY6dUVvNGRrc0FSTXFfQlNIYUFIVXI4UQ==',
|
||||||
|
ver: '8.0.0',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not decode an invalid token', () => {
|
||||||
|
expect(decodeEnrollmentToken(JSON.stringify(token))).toBeUndefined();
|
||||||
|
expect(
|
||||||
|
decodeEnrollmentToken(
|
||||||
|
btoa(
|
||||||
|
JSON.stringify({
|
||||||
|
ver: [''],
|
||||||
|
adr: null,
|
||||||
|
fgr: false,
|
||||||
|
key: undefined,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
)
|
||||||
|
).toBeUndefined();
|
||||||
|
expect(decodeEnrollmentToken(btoa(JSON.stringify({})))).toBeUndefined();
|
||||||
|
expect(decodeEnrollmentToken(btoa(JSON.stringify([])))).toBeUndefined();
|
||||||
|
expect(decodeEnrollmentToken(btoa(JSON.stringify(null)))).toBeUndefined();
|
||||||
|
expect(decodeEnrollmentToken(btoa(JSON.stringify('')))).toBeUndefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
91
src/cli_setup/utils.ts
Normal file
91
src/cli_setup/utils.ts
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
/*
|
||||||
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||||
|
* or more contributor license agreements. Licensed under the Elastic License
|
||||||
|
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||||
|
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||||
|
* Side Public License, v 1.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { getConfigPath } from '@kbn/utils';
|
||||||
|
import inquirer from 'inquirer';
|
||||||
|
import { duration } from 'moment';
|
||||||
|
import { merge } from 'lodash';
|
||||||
|
|
||||||
|
import { Logger } from '../core/server';
|
||||||
|
import { ClusterClient } from '../core/server/elasticsearch/client';
|
||||||
|
import { configSchema } from '../core/server/elasticsearch';
|
||||||
|
import { ElasticsearchService } from '../plugins/interactive_setup/server/elasticsearch_service';
|
||||||
|
import { KibanaConfigWriter } from '../plugins/interactive_setup/server/kibana_config_writer';
|
||||||
|
import type { EnrollmentToken } from '../plugins/interactive_setup/common';
|
||||||
|
|
||||||
|
const noop = () => {};
|
||||||
|
const logger: Logger = {
|
||||||
|
debug: noop,
|
||||||
|
error: noop,
|
||||||
|
warn: noop,
|
||||||
|
trace: noop,
|
||||||
|
info: noop,
|
||||||
|
fatal: noop,
|
||||||
|
log: noop,
|
||||||
|
get: () => logger,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const kibanaConfigWriter = new KibanaConfigWriter(getConfigPath(), logger);
|
||||||
|
export const elasticsearch = new ElasticsearchService(logger).setup({
|
||||||
|
connectionCheckInterval: duration(Infinity),
|
||||||
|
elasticsearch: {
|
||||||
|
createClient: (type, config) => {
|
||||||
|
const defaults = configSchema.validate({});
|
||||||
|
return new ClusterClient(
|
||||||
|
merge(
|
||||||
|
defaults,
|
||||||
|
{
|
||||||
|
hosts: Array.isArray(defaults.hosts) ? defaults.hosts : [defaults.hosts],
|
||||||
|
},
|
||||||
|
config
|
||||||
|
),
|
||||||
|
logger,
|
||||||
|
type
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export async function promptToken() {
|
||||||
|
const answers = await inquirer.prompt({
|
||||||
|
type: 'input',
|
||||||
|
name: 'token',
|
||||||
|
message: 'Enter enrollment token:',
|
||||||
|
validate: (value = '') => (decodeEnrollmentToken(value) ? true : 'Invalid enrollment token'),
|
||||||
|
});
|
||||||
|
return answers.token;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function decodeEnrollmentToken(enrollmentToken: string): EnrollmentToken | undefined {
|
||||||
|
try {
|
||||||
|
const json = JSON.parse(atob(enrollmentToken)) as EnrollmentToken;
|
||||||
|
if (
|
||||||
|
!Array.isArray(json.adr) ||
|
||||||
|
json.adr.some((adr) => typeof adr !== 'string') ||
|
||||||
|
typeof json.fgr !== 'string' ||
|
||||||
|
typeof json.key !== 'string' ||
|
||||||
|
typeof json.ver !== 'string'
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
return { ...json, adr: json.adr.map((adr) => `https://${adr}`), key: btoa(json.key) };
|
||||||
|
} catch (error) {} // eslint-disable-line no-empty
|
||||||
|
}
|
||||||
|
|
||||||
|
function btoa(str: string) {
|
||||||
|
return Buffer.from(str, 'binary').toString('base64');
|
||||||
|
}
|
||||||
|
|
||||||
|
function atob(str: string) {
|
||||||
|
return Buffer.from(str, 'base64').toString('binary');
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getCommand(command: string, args?: string) {
|
||||||
|
const isWindows = process.platform === 'win32';
|
||||||
|
return `${isWindows ? `bin\\${command}.bat` : `bin/${command}`}${args ? ` ${args}` : ''}`;
|
||||||
|
}
|
29
src/dev/build/tasks/bin/scripts/kibana-setup
Executable file
29
src/dev/build/tasks/bin/scripts/kibana-setup
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_setup/dist" "$@"
|
35
src/dev/build/tasks/bin/scripts/kibana-setup.bat
Executable file
35
src/dev/build/tasks/bin/scripts/kibana-setup.bat
Executable file
|
@ -0,0 +1,35 @@
|
||||||
|
@echo off
|
||||||
|
|
||||||
|
SETLOCAL ENABLEDELAYEDEXPANSION
|
||||||
|
|
||||||
|
set SCRIPT_DIR=%~dp0
|
||||||
|
for %%I in ("%SCRIPT_DIR%..") do set DIR=%%~dpfI
|
||||||
|
|
||||||
|
set NODE=%DIR%\node\node.exe
|
||||||
|
|
||||||
|
If Not Exist "%NODE%" (
|
||||||
|
Echo unable to find usable node.js executable.
|
||||||
|
Exit /B 1
|
||||||
|
)
|
||||||
|
|
||||||
|
set CONFIG_DIR=%KBN_PATH_CONF%
|
||||||
|
If ["%KBN_PATH_CONF%"] == [] (
|
||||||
|
set "CONFIG_DIR=%DIR%\config"
|
||||||
|
)
|
||||||
|
|
||||||
|
IF EXIST "%CONFIG_DIR%\node.options" (
|
||||||
|
for /F "usebackq eol=# tokens=*" %%i in ("%CONFIG_DIR%\node.options") do (
|
||||||
|
If [!NODE_OPTIONS!] == [] (
|
||||||
|
set "NODE_OPTIONS=%%i"
|
||||||
|
) Else (
|
||||||
|
set "NODE_OPTIONS=!NODE_OPTIONS! %%i"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
TITLE Kibana Setup
|
||||||
|
"%NODE%" "%DIR%\src\cli_setup\dist" %*
|
||||||
|
|
||||||
|
:finally
|
||||||
|
|
||||||
|
ENDLOCAL
|
|
@ -26,7 +26,7 @@ export const CopySource: Task = {
|
||||||
'!src/test_utils/**',
|
'!src/test_utils/**',
|
||||||
'!src/fixtures/**',
|
'!src/fixtures/**',
|
||||||
'!src/cli/repl/**',
|
'!src/cli/repl/**',
|
||||||
'!src/cli/dev.js',
|
'!src/cli*/dev.js',
|
||||||
'!src/functional_test_runner/**',
|
'!src/functional_test_runner/**',
|
||||||
'!src/dev/**',
|
'!src/dev/**',
|
||||||
'!**/jest.config.js',
|
'!**/jest.config.js',
|
||||||
|
|
|
@ -22,11 +22,7 @@ async function getDependencies(cwd: string, entries: string[]) {
|
||||||
export async function findUsedDependencies(listedPkgDependencies: any, baseDir: any) {
|
export async function findUsedDependencies(listedPkgDependencies: any, baseDir: any) {
|
||||||
// Define the entry points for the server code in order to
|
// Define the entry points for the server code in order to
|
||||||
// start here later looking for the server side dependencies
|
// start here later looking for the server side dependencies
|
||||||
const mainCodeEntries = [
|
const mainCodeEntries = await globby(normalize(Path.resolve(baseDir, `src/cli*/dist.js`)));
|
||||||
Path.resolve(baseDir, `src/cli/dist.js`),
|
|
||||||
Path.resolve(baseDir, `src/cli_keystore/dist.js`),
|
|
||||||
Path.resolve(baseDir, `src/cli_plugin/dist.js`),
|
|
||||||
];
|
|
||||||
|
|
||||||
const discoveredPluginEntries = await globby([
|
const discoveredPluginEntries = await globby([
|
||||||
normalize(Path.resolve(baseDir, `src/plugins/**/server/index.js`)),
|
normalize(Path.resolve(baseDir, `src/plugins/**/server/index.js`)),
|
||||||
|
|
|
@ -193,7 +193,7 @@ const EnrollmentTokenDetails: FunctionComponent<EnrollmentTokenDetailsProps> = (
|
||||||
</EuiText>
|
</EuiText>
|
||||||
);
|
);
|
||||||
|
|
||||||
export function decodeEnrollmentToken(enrollmentToken: string) {
|
export function decodeEnrollmentToken(enrollmentToken: string): EnrollmentToken | undefined {
|
||||||
try {
|
try {
|
||||||
const json = JSON.parse(atob(enrollmentToken)) as EnrollmentToken;
|
const json = JSON.parse(atob(enrollmentToken)) as EnrollmentToken;
|
||||||
if (
|
if (
|
||||||
|
|
|
@ -34,7 +34,7 @@ import { getDetailedErrorMessage, getErrorStatusCode } from './errors';
|
||||||
|
|
||||||
export interface EnrollParameters {
|
export interface EnrollParameters {
|
||||||
apiKey: string;
|
apiKey: string;
|
||||||
hosts: string[];
|
hosts: readonly string[];
|
||||||
caFingerprint: string;
|
caFingerprint: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,7 +49,7 @@ export interface ElasticsearchServiceSetupDeps {
|
||||||
/**
|
/**
|
||||||
* Core Elasticsearch service preboot contract;
|
* Core Elasticsearch service preboot contract;
|
||||||
*/
|
*/
|
||||||
elasticsearch: ElasticsearchServicePreboot;
|
elasticsearch: Pick<ElasticsearchServicePreboot, 'createClient'>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interval for the Elasticsearch connection check (whether it's configured or not).
|
* Interval for the Elasticsearch connection check (whether it's configured or not).
|
||||||
|
@ -169,7 +169,7 @@ export class ElasticsearchService {
|
||||||
* the Elasticsearch node we're enrolling with. Should be in a form of a hex colon-delimited string in upper case.
|
* the Elasticsearch node we're enrolling with. Should be in a form of a hex colon-delimited string in upper case.
|
||||||
*/
|
*/
|
||||||
private async enroll(
|
private async enroll(
|
||||||
elasticsearch: ElasticsearchServicePreboot,
|
elasticsearch: Pick<ElasticsearchServicePreboot, 'createClient'>,
|
||||||
{ apiKey, hosts, caFingerprint }: EnrollParameters
|
{ apiKey, hosts, caFingerprint }: EnrollParameters
|
||||||
) {
|
) {
|
||||||
const scopeableRequest: ScopeableRequest = { headers: { authorization: `ApiKey ${apiKey}` } };
|
const scopeableRequest: ScopeableRequest = { headers: { authorization: `ApiKey ${apiKey}` } };
|
||||||
|
@ -257,7 +257,7 @@ export class ElasticsearchService {
|
||||||
}
|
}
|
||||||
|
|
||||||
private async authenticate(
|
private async authenticate(
|
||||||
elasticsearch: ElasticsearchServicePreboot,
|
elasticsearch: Pick<ElasticsearchServicePreboot, 'createClient'>,
|
||||||
{ host, username, password, caCert }: AuthenticateParameters
|
{ host, username, password, caCert }: AuthenticateParameters
|
||||||
) {
|
) {
|
||||||
const client = elasticsearch.createClient('authenticate', {
|
const client = elasticsearch.createClient('authenticate', {
|
||||||
|
@ -281,7 +281,10 @@ export class ElasticsearchService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async ping(elasticsearch: ElasticsearchServicePreboot, host: string) {
|
private async ping(
|
||||||
|
elasticsearch: Pick<ElasticsearchServicePreboot, 'createClient'>,
|
||||||
|
host: string
|
||||||
|
) {
|
||||||
const client = elasticsearch.createClient('ping', {
|
const client = elasticsearch.createClient('ping', {
|
||||||
hosts: [host],
|
hosts: [host],
|
||||||
username: '',
|
username: '',
|
||||||
|
@ -393,4 +396,15 @@ export class ElasticsearchService {
|
||||||
.replace(/([^\n]{1,65})/g, '$1\n')
|
.replace(/([^\n]{1,65})/g, '$1\n')
|
||||||
.replace(/\n$/g, '')}\n-----END CERTIFICATE-----\n`;
|
.replace(/\n$/g, '')}\n-----END CERTIFICATE-----\n`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static formatFingerprint(caFingerprint: string) {
|
||||||
|
// Convert a plain hex string returned in the enrollment token to a format that ES client
|
||||||
|
// expects, i.e. to a colon delimited hex string in upper case: deadbeef -> DE:AD:BE:EF.
|
||||||
|
return (
|
||||||
|
caFingerprint
|
||||||
|
.toUpperCase()
|
||||||
|
.match(/.{1,2}/g)
|
||||||
|
?.join(':') ?? ''
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@ import {
|
||||||
ERROR_KIBANA_CONFIG_NOT_WRITABLE,
|
ERROR_KIBANA_CONFIG_NOT_WRITABLE,
|
||||||
ERROR_OUTSIDE_PREBOOT_STAGE,
|
ERROR_OUTSIDE_PREBOOT_STAGE,
|
||||||
} from '../../common';
|
} from '../../common';
|
||||||
|
import { ElasticsearchService } from '../elasticsearch_service';
|
||||||
import type { EnrollResult } from '../elasticsearch_service';
|
import type { EnrollResult } from '../elasticsearch_service';
|
||||||
import type { WriteConfigParameters } from '../kibana_config_writer';
|
import type { WriteConfigParameters } from '../kibana_config_writer';
|
||||||
import type { RouteDefinitionParams } from './';
|
import type { RouteDefinitionParams } from './';
|
||||||
|
@ -92,20 +93,12 @@ export function defineEnrollRoutes({
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert a plain hex string returned in the enrollment token to a format that ES client
|
|
||||||
// expects, i.e. to a colon delimited hex string in upper case: deadbeef -> DE:AD:BE:EF.
|
|
||||||
const colonFormattedCaFingerprint =
|
|
||||||
request.body.caFingerprint
|
|
||||||
.toUpperCase()
|
|
||||||
.match(/.{1,2}/g)
|
|
||||||
?.join(':') ?? '';
|
|
||||||
|
|
||||||
let configToWrite: WriteConfigParameters & EnrollResult;
|
let configToWrite: WriteConfigParameters & EnrollResult;
|
||||||
try {
|
try {
|
||||||
configToWrite = await elasticsearch.enroll({
|
configToWrite = await elasticsearch.enroll({
|
||||||
apiKey: request.body.apiKey,
|
apiKey: request.body.apiKey,
|
||||||
hosts: request.body.hosts,
|
hosts: request.body.hosts,
|
||||||
caFingerprint: colonFormattedCaFingerprint,
|
caFingerprint: ElasticsearchService.formatFingerprint(request.body.caFingerprint),
|
||||||
});
|
});
|
||||||
} catch {
|
} catch {
|
||||||
// For security reasons, we shouldn't leak to the user whether Elasticsearch node couldn't process enrollment
|
// For security reasons, we shouldn't leak to the user whether Elasticsearch node couldn't process enrollment
|
||||||
|
|
|
@ -8,6 +8,8 @@
|
||||||
"typings/**/*",
|
"typings/**/*",
|
||||||
|
|
||||||
"src/cli/**/*",
|
"src/cli/**/*",
|
||||||
|
"src/cli_setup/**/*",
|
||||||
|
"src/cli_plugin/**/*",
|
||||||
"src/dev/**/*",
|
"src/dev/**/*",
|
||||||
"src/fixtures/**/*",
|
"src/fixtures/**/*",
|
||||||
|
|
||||||
|
@ -17,6 +19,7 @@
|
||||||
"references": [
|
"references": [
|
||||||
{ "path": "./src/core/tsconfig.json" },
|
{ "path": "./src/core/tsconfig.json" },
|
||||||
{ "path": "./src/plugins/usage_collection/tsconfig.json" },
|
{ "path": "./src/plugins/usage_collection/tsconfig.json" },
|
||||||
|
{ "path": "./src/plugins/interactive_setup/tsconfig.json" },
|
||||||
{ "path": "./x-pack/plugins/reporting/tsconfig.json" },
|
{ "path": "./x-pack/plugins/reporting/tsconfig.json" },
|
||||||
]
|
]
|
||||||
}
|
}
|
|
@ -10806,11 +10806,6 @@ commander@2.17.x, commander@~2.17.1:
|
||||||
resolved "https://registry.yarnpkg.com/commander/-/commander-2.17.1.tgz#bd77ab7de6de94205ceacc72f1716d29f20a77bf"
|
resolved "https://registry.yarnpkg.com/commander/-/commander-2.17.1.tgz#bd77ab7de6de94205ceacc72f1716d29f20a77bf"
|
||||||
integrity sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==
|
integrity sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==
|
||||||
|
|
||||||
commander@^3.0.2:
|
|
||||||
version "3.0.2"
|
|
||||||
resolved "https://registry.yarnpkg.com/commander/-/commander-3.0.2.tgz#6837c3fb677ad9933d1cfba42dd14d5117d6b39e"
|
|
||||||
integrity sha512-Gar0ASD4BDyKC4hl4DwHqDrmvjoxWKZigVnAbn5H1owvm4CxCPdb0HQDehwNYMJpla5+M2tPmPARzhtYuwpHow==
|
|
||||||
|
|
||||||
commander@^4.0.1, commander@^4.1.1:
|
commander@^4.0.1, commander@^4.1.1:
|
||||||
version "4.1.1"
|
version "4.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068"
|
resolved "https://registry.yarnpkg.com/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068"
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue