diff --git a/packages/kbn-dev-utils/package.json b/packages/kbn-dev-utils/package.json index 5cd347f9808b3..71c7d2e311017 100644 --- a/packages/kbn-dev-utils/package.json +++ b/packages/kbn-dev-utils/package.json @@ -6,7 +6,7 @@ "license": "Apache-2.0", "private": true, "scripts": { - "build": "babel src --out-dir target", + "build": "babel src --out-dir target --copy-files", "kbn:bootstrap": "yarn build --quiet", "kbn:watch": "yarn build --watch" }, diff --git a/packages/kbn-dev-utils/src/certs/ca.crt b/packages/kbn-dev-utils/src/certs/ca.crt new file mode 100755 index 0000000000000..3e964823c5086 --- /dev/null +++ b/packages/kbn-dev-utils/src/certs/ca.crt @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDSjCCAjKgAwIBAgIVAOgxLlE1RMGl2fYgTKDznvDL2vboMA0GCSqGSIb3DQEB +CwUAMDQxMjAwBgNVBAMTKUVsYXN0aWMgQ2VydGlmaWNhdGUgVG9vbCBBdXRvZ2Vu +ZXJhdGVkIENBMB4XDTE5MDcxMTE3MzQ0OFoXDTIyMDcxMDE3MzQ0OFowNDEyMDAG +A1UEAxMpRWxhc3RpYyBDZXJ0aWZpY2F0ZSBUb29sIEF1dG9nZW5lcmF0ZWQgQ0Ew +ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCNImKp/A9l++Ac7U5lvHOA ++fYRb8p7AgdfKBMB0v3bo+bpHjkbkf3vYHjo1xJSg5ls6EPK+Do4owkAgKJdrznI +5/efJOjgA+ylH4rgAfrRIQmiFEWZnAv86vJ+Iq83mfkPELb4dvXCi7AFQkzoM/rY +Lbi97xha5bA2SEmpYp7VhBTM9zWy+q9Tm5odPO8u2n75GpIM2RwipaXlL0ink+06 +/oweQJoivaDgpDOmUXCFPmpV3VCdhUGxDQPyG0upQkF+NbQoei4RmluPEmVz4S7I +TFLWjX7LeZVP63bJkcCgiq6Hm97kDtr9EYlPKhHm7UMWzhNzHbfvySMDzqAJC0KX +AgMBAAGjUzBRMB0GA1UdDgQWBBRKqaaQ/+jT+ipPLJe7qekp1N/zizAfBgNVHSME +GDAWgBRKqaaQ/+jT+ipPLJe7qekp1N/zizAPBgNVHRMBAf8EBTADAQH/MA0GCSqG +SIb3DQEBCwUAA4IBAQA7Gcq8h8yDXvepfKUAcTTMCBZkI+g3qE1gfRwjW7587CIj +xnrzEqANU+Q1lv7IeQ158HiduDUMZfnvpuNwkf0HkqnRWb57RwfVdCAlAeZmzipq +5ZJWlIW4dbmk57nGLg4fCszedi0uSGytZ2/BUdpWyC0fAM97h7Agtr4xGGKMEL67 +uB55ijt61V62HZ5wWXWNO9m+wfmdnt+YQViQJHtpYz1oOmWhY3dpitZLfWs1sLLD +w3CZOhmWX7+P7+HlCkSBF4swzHOCI3THyX61NbLxju8VkTAjwbZPq4EOnVKnO6kr +RdwQVnzKnqG5fxfSGknNahy0pOhJHZlGLwECRlgF +-----END CERTIFICATE----- diff --git a/packages/kbn-dev-utils/src/certs/elasticsearch.crt b/packages/kbn-dev-utils/src/certs/elasticsearch.crt new file mode 100755 index 0000000000000..b30e11e9bbce1 --- /dev/null +++ b/packages/kbn-dev-utils/src/certs/elasticsearch.crt @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDRDCCAiygAwIBAgIVAI8V1fwvXKykKtp5k0cLpTOtY+DVMA0GCSqGSIb3DQEB +CwUAMDQxMjAwBgNVBAMTKUVsYXN0aWMgQ2VydGlmaWNhdGUgVG9vbCBBdXRvZ2Vu +ZXJhdGVkIENBMB4XDTE5MDcxMTE3MzUxOFoXDTIyMDcxMDE3MzUxOFowGDEWMBQG +A1UEAxMNZWxhc3RpY3NlYXJjaDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBALW+8gV6m6wYmTZmrXzNWKElE+ePkkikCviNfuWonWqxgAoWpAwAx2FvdhP3 +UDFbe38ydJX4oDgXeC25vdIR6z2uqzx+GXSNSybO7luuOUYQOP4Xf5Cj3zzXXMyu +nY1nZTVsChI9jAMz4cZZdUd04f4r4TBNxrFCcVR0uec5RGRXuP8rSQd9AbYFUVYf +jJeLb24asghb2Ku+c2JGvMqPEXFWFGOXFhUoIbRjCJNTDcr1ZXPof3+fO1l6HmhT +QBSqC4IZL8XqANltDT4tCQDD8L9+ckWJD8MP3wPkPUGZId2gLu++hrb9YfiP2upq +N/f3P7l5Fcisw1iwQC4+DGMTyfcCAwEAAaNpMGcwHQYDVR0OBBYEFGuiGk8HLpG2 +MyA24/J+GwxT32ikMB8GA1UdIwQYMBaAFEqpppD/6NP6Kk8sl7up6SnU3/OLMBoG +A1UdEQQTMBGCCWxvY2FsaG9zdIcEfwAAATAJBgNVHRMEAjAAMA0GCSqGSIb3DQEB +CwUAA4IBAQB8yfY0edAgq2KnJNWyl8NpHNfqtM27+/LR2V8OxVwxV1hc4ZilczLu +CXeqP9uqBVjcck6fvLrjy4LhSG0V05j51UMJ1FjFVTBuhlrDcd3j8848yWrmyz8z +vPYYY2vIN9d1NsBgufULwliBT4UJchsYE8xT5ayAzGHKCTlzHGHMTPzYjwac8nbT +nd2u+6h0OQOJn6K4v+RfXtN4EA8ZUrYxUkqHNS3cFB5sxH7JQGi25XJc5MfxyCwY +YOukxbN85ew861N6oVd+W+nGJu8WOLU88/uvCv+dLhnAlnnIOLqvmrD5m7gFsFO9 +Z7Xz/U1SbNipWy9OLOhqq2Ja59j8p9e5 +-----END CERTIFICATE----- diff --git a/packages/kbn-dev-utils/src/certs/elasticsearch.key b/packages/kbn-dev-utils/src/certs/elasticsearch.key new file mode 100755 index 0000000000000..1013ce3971246 --- /dev/null +++ b/packages/kbn-dev-utils/src/certs/elasticsearch.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEAtb7yBXqbrBiZNmatfM1YoSUT54+SSKQK+I1+5aidarGAChak +DADHYW92E/dQMVt7fzJ0lfigOBd4Lbm90hHrPa6rPH4ZdI1LJs7uW645RhA4/hd/ +kKPfPNdczK6djWdlNWwKEj2MAzPhxll1R3Th/ivhME3GsUJxVHS55zlEZFe4/ytJ +B30BtgVRVh+Ml4tvbhqyCFvYq75zYka8yo8RcVYUY5cWFSghtGMIk1MNyvVlc+h/ +f587WXoeaFNAFKoLghkvxeoA2W0NPi0JAMPwv35yRYkPww/fA+Q9QZkh3aAu776G +tv1h+I/a6mo39/c/uXkVyKzDWLBALj4MYxPJ9wIDAQABAoIBAQCb1ggrjn/gxo7I +yK3FL0XplqNEkCR8SLxndtvyC+w+Schh3hv3dst+zlXOtOZ8C9cOr7KrzS2EKwuP +GY6bi2XL0/NbwTwOZgCkXBahYfgWDV7w8DEfUoPd5UPa9XZ+gsOTVPolvcRKErhq +nNYk2SHWEMXb5zSRVUlbg2LL0pzD88bIuKJX+FwPvWcQc2P4OdVTq77iedcl82zZ +6PqTNqKMep7/odLQeBfX7OapOAviVnPYHe0TA114COOimR/pK8IA1OJymX5rgU7O +Wh+uNBSxdHsTTYTkAvw8Bt5Q8n1WCpQwZoYU3xWuSlu7eJ7kcgdFOu9r9GjSXysT +UYCd8s0BAoGBAPXPpCDRxjqF3/ToZ5x5dorKxxJyrmldzMJaUjqOv7y6kezbdBql +n7p3AJ5UfYUW/N6pgQXaWF4MPSyj7ItHhwHjL+v0Manmi5gq8oA30fplhjUlPre7 +Lx4v7SEmH739EHrkZ2ClIQwY3wKuN8mZKgw6RseFgphczDmhHCqEbjW3AoGBAL1H +fkl0RNdZ3nZg0u7MUVk8ytnqBsp7bNFhEs0zUl7ghu3NLaPt8qhirG638oMSCxqH +FPeM3/DryokQAym+UHYNMwiBziEUB2CKMMj7S5YFFWIldCxFeImCO2EP+y3hmbTZ +yjsznNrDzQtErZGP+JTRZcy9xF0oAfVt0G/O1Q3BAoGAa8bqINW5g6l1Q82uuEXt +evdkB6uu21YcVE8D5Nb4LMjk+KRUKObbvQc2hzVmf7dPklVh0+4jdsEJBYyuR3dK +M8KoHV3JdMQ4CrUx9JQFBjQDf0PgVvDEvQiogTNVEZlm42tIBHECp2o0RdmbblIw +xIG8zPi2BRYTGWWRkvbT18sCgYA+c/B/XBW62LRGavwuPsw4nY5xCH7lIIRvMZB6 +lIyBMaRToneEt2ZxmN08SwWBqdpwDlIkvB7H54UUZGwmwdzaltBX5jyVPX6RpAck +yYXPIi5EDAeg8+sptAbTp+pA4UdOHO5VSlpe9GwbY7XBabejotPsElFQS3sZ9/nm +amByAQKBgQCJWghllys1qk76/6PmeVjwjaK9n8o+94LWhqODXlACmDRyse5dwpYb +BIsMMZrNu1YsqDXlWpU7xNa6A8j4oa+EPnm/01PjdueAvMB/oE1woawM5tSsd8NQ +zeQPDhxjDxzaO5l4oJLZg6FT7iQAprhYZjgb8m1vz0D2Xid0A3Kgpw== +-----END RSA PRIVATE KEY----- diff --git a/packages/kbn-dev-utils/src/certs/index.js b/packages/kbn-dev-utils/src/certs/index.js new file mode 100644 index 0000000000000..97292eec3512b --- /dev/null +++ b/packages/kbn-dev-utils/src/certs/index.js @@ -0,0 +1,24 @@ +/* + * 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 { resolve } from 'path'; + +export const CA_CERT_PATH = resolve(__dirname, 'ca.crt'); +export const ES_KEY_PATH = resolve(__dirname, 'elasticsearch.key'); +export const ES_CERT_PATH = resolve(__dirname, 'elasticsearch.crt'); diff --git a/packages/kbn-dev-utils/src/index.js b/packages/kbn-dev-utils/src/index.js index 82492e568f4b5..cba24575bd39c 100644 --- a/packages/kbn-dev-utils/src/index.js +++ b/packages/kbn-dev-utils/src/index.js @@ -20,3 +20,4 @@ export { withProcRunner } from './proc_runner'; export { ToolingLog, ToolingLogTextWriter, pickLevelFromFlags } from './tooling_log'; export { createAbsolutePathSerializer } from './serializers'; +export { CA_CERT_PATH, ES_KEY_PATH, ES_CERT_PATH } from './certs'; diff --git a/packages/kbn-es/src/cli_commands/archive.js b/packages/kbn-es/src/cli_commands/archive.js index 273cdea87c67c..a3197d959fc8e 100644 --- a/packages/kbn-es/src/cli_commands/archive.js +++ b/packages/kbn-es/src/cli_commands/archive.js @@ -36,6 +36,7 @@ exports.help = (defaults = {}) => { --install-path Installation path, defaults to 'source' within base-path --password Sets password for elastic user [default: ${password}] --password.[user] Sets password for native realm user [default: ${password}] + --ssl Sets up SSL on Elasticsearch -E Additional key=value settings to pass to Elasticsearch Example: @@ -56,7 +57,7 @@ exports.run = async (defaults = {}) => { default: defaults, }); - const cluster = new Cluster(); + const cluster = new Cluster({ ssl: options.ssl }); const [, path] = options._; if (!path || !path.endsWith('tar.gz')) { diff --git a/packages/kbn-es/src/cli_commands/snapshot.js b/packages/kbn-es/src/cli_commands/snapshot.js index bc8aee7696a1f..3139cb18793ff 100644 --- a/packages/kbn-es/src/cli_commands/snapshot.js +++ b/packages/kbn-es/src/cli_commands/snapshot.js @@ -38,6 +38,7 @@ exports.help = (defaults = {}) => { --password.[user] Sets password for native realm user [default: ${password}] -E Additional key=value settings to pass to Elasticsearch --download-only Download the snapshot but don't actually start it + --ssl Sets up SSL on Elasticsearch Example: @@ -62,7 +63,7 @@ exports.run = async (defaults = {}) => { default: defaults, }); - const cluster = new Cluster(); + const cluster = new Cluster({ ssl: options.ssl }); if (options['download-only']) { await cluster.downloadSnapshot(options); } else { diff --git a/packages/kbn-es/src/cli_commands/source.js b/packages/kbn-es/src/cli_commands/source.js index 3065ca887bfff..5ea95add4ef59 100644 --- a/packages/kbn-es/src/cli_commands/source.js +++ b/packages/kbn-es/src/cli_commands/source.js @@ -36,6 +36,7 @@ exports.help = (defaults = {}) => { --data-archive Path to zip or tarball containing an ES data directory to seed the cluster with. --password Sets password for elastic user [default: ${password}] --password.[user] Sets password for native realm user [default: ${password}] + --ssl Sets up SSL on Elasticsearch -E Additional key=value settings to pass to Elasticsearch Example: @@ -58,7 +59,7 @@ exports.run = async (defaults = {}) => { default: defaults, }); - const cluster = new Cluster(); + const cluster = new Cluster({ ssl: options.ssl }); const { installPath } = await cluster.installSource(options); if (options.dataArchive) { diff --git a/packages/kbn-es/src/cluster.js b/packages/kbn-es/src/cluster.js index e602fbe25d0f2..2c81035d5c25c 100644 --- a/packages/kbn-es/src/cluster.js +++ b/packages/kbn-es/src/cluster.js @@ -17,6 +17,8 @@ * under the License. */ +const fs = require('fs'); +const util = require('util'); const execa = require('execa'); const chalk = require('chalk'); const path = require('path'); @@ -33,6 +35,8 @@ const { createCliError } = require('./errors'); const { promisify } = require('util'); const treeKillAsync = promisify(require('tree-kill')); const { parseSettings, SettingsFilter } = require('./settings'); +const { CA_CERT_PATH, ES_KEY_PATH, ES_CERT_PATH } = require('@kbn/dev-utils'); +const readFile = util.promisify(fs.readFile); // listen to data on stream until map returns anything but undefined const first = (stream, map) => @@ -48,8 +52,10 @@ const first = (stream, map) => }); exports.Cluster = class Cluster { - constructor(log = defaultLog) { + constructor({ log = defaultLog, ssl = false } = {}) { this._log = log; + this._ssl = ssl; + this._caCertPromise = ssl ? readFile(CA_CERT_PATH) : undefined; } /** @@ -251,10 +257,18 @@ exports.Cluster = class Cluster { this._log.info(chalk.bold('Starting')); this._log.indent(4); - const args = parseSettings( - extractConfigFiles(options.esArgs || [], installPath, { log: this._log }), - { filter: SettingsFilter.NonSecureOnly } - ).reduce( + // Add to esArgs if ssl is enabled + const esArgs = [].concat(options.esArgs || []); + if (this._ssl) { + esArgs.push('xpack.security.http.ssl.enabled=true'); + esArgs.push(`xpack.security.http.ssl.key=${ES_KEY_PATH}`); + esArgs.push(`xpack.security.http.ssl.certificate=${ES_CERT_PATH}`); + esArgs.push(`xpack.security.http.ssl.certificate_authorities=${CA_CERT_PATH}`); + } + + const args = parseSettings(extractConfigFiles(esArgs, installPath, { log: this._log }), { + filter: SettingsFilter.NonSecureOnly, + }).reduce( (acc, [settingName, settingValue]) => acc.concat(['-E', `${settingName}=${settingValue}`]), [] ); @@ -282,7 +296,14 @@ exports.Cluster = class Cluster { // once the http port is available setup the native realm this._nativeRealmSetup = httpPort.then(async port => { - const nativeRealm = new NativeRealm(options.password, port, this._log); + const caCert = await this._caCertPromise; + const nativeRealm = new NativeRealm({ + port, + caCert, + log: this._log, + elasticPassword: options.password, + ssl: this._ssl, + }); await nativeRealm.setPasswords(options); }); diff --git a/packages/kbn-es/src/integration_tests/__fixtures__/es_bin.js b/packages/kbn-es/src/integration_tests/__fixtures__/es_bin.js index 6c49371fd7d40..d3181a748ffbb 100644 --- a/packages/kbn-es/src/integration_tests/__fixtures__/es_bin.js +++ b/packages/kbn-es/src/integration_tests/__fixtures__/es_bin.js @@ -19,9 +19,11 @@ * under the License. */ -const { createServer } = require('http'); +const fs = require('fs'); const { format: formatUrl } = require('url'); -const { exitCode, start } = JSON.parse(process.argv[2]); +const { exitCode, start, ssl } = JSON.parse(process.argv[2]); +const { createServer } = ssl ? require('https') : require('http'); +const { ES_KEY_PATH, ES_CERT_PATH } = require('@kbn/dev-utils'); process.exitCode = exitCode; @@ -30,27 +32,33 @@ if (!start) { } let serverUrl; -const server = createServer((req, res) => { - const url = new URL(req.url, serverUrl); - const send = (code, body) => { - res.writeHead(code, { 'content-type': 'application/json' }); - res.end(JSON.stringify(body)); - }; +const server = createServer( + { + key: ssl ? fs.readFileSync(ES_KEY_PATH) : undefined, + cert: ssl ? fs.readFileSync(ES_CERT_PATH) : undefined, + }, + (req, res) => { + const url = new URL(req.url, serverUrl); + const send = (code, body) => { + res.writeHead(code, { 'content-type': 'application/json' }); + res.end(JSON.stringify(body)); + }; - if (url.pathname === '/_xpack') { - return send(400, { + if (url.pathname === '/_xpack') { + return send(400, { + error: { + reason: 'foo bar', + }, + }); + } + + return send(404, { error: { - reason: 'foo bar', + reason: 'not found', }, }); } - - return send(404, { - error: { - reason: 'not found', - }, - }); -}); +); // setup server auto close after 1 second of silence let serverCloseTimer; diff --git a/packages/kbn-es/src/integration_tests/cluster.test.js b/packages/kbn-es/src/integration_tests/cluster.test.js index e74f94d92beee..dd570e27e3282 100644 --- a/packages/kbn-es/src/integration_tests/cluster.test.js +++ b/packages/kbn-es/src/integration_tests/cluster.test.js @@ -17,10 +17,11 @@ * under the License. */ -const { ToolingLog } = require('@kbn/dev-utils'); +const { ToolingLog, CA_CERT_PATH, ES_KEY_PATH, ES_CERT_PATH } = require('@kbn/dev-utils'); const execa = require('execa'); const { Cluster } = require('../cluster'); const { installSource, installSnapshot, installArchive } = require('../install'); +const { extractConfigFiles } = require('../utils/extract_config_files'); jest.mock('../install', () => ({ installSource: jest.fn(), @@ -29,6 +30,9 @@ jest.mock('../install', () => ({ })); jest.mock('execa', () => jest.fn()); +jest.mock('../utils/extract_config_files', () => ({ + extractConfigFiles: jest.fn(), +})); const log = new ToolingLog(); @@ -63,6 +67,7 @@ function mockEsBin({ exitCode, start }) { JSON.stringify({ exitCode, start, + ssl: args.includes('xpack.security.http.ssl.enabled=true'), }), ], options @@ -72,6 +77,7 @@ function mockEsBin({ exitCode, start }) { beforeEach(() => { jest.resetAllMocks(); + extractConfigFiles.mockImplementation(config => config); }); describe('#installSource()', () => { @@ -86,7 +92,7 @@ describe('#installSource()', () => { }) ); - const cluster = new Cluster(log); + const cluster = new Cluster({ log }); const promise = cluster.installSource(); await ensureNoResolve(promise); resolveInstallSource(); @@ -97,7 +103,7 @@ describe('#installSource()', () => { it('passes through all options+log to installSource()', async () => { installSource.mockResolvedValue({}); - const cluster = new Cluster(log); + const cluster = new Cluster({ log }); await cluster.installSource({ foo: 'bar' }); expect(installSource).toHaveBeenCalledTimes(1); expect(installSource).toHaveBeenCalledWith({ @@ -108,7 +114,7 @@ describe('#installSource()', () => { it('rejects if installSource() rejects', async () => { installSource.mockRejectedValue(new Error('foo')); - const cluster = new Cluster(log); + const cluster = new Cluster({ log }); await expect(cluster.installSource()).rejects.toThrowError('foo'); }); }); @@ -125,7 +131,7 @@ describe('#installSnapshot()', () => { }) ); - const cluster = new Cluster(log); + const cluster = new Cluster({ log }); const promise = cluster.installSnapshot(); await ensureNoResolve(promise); resolveInstallSnapshot(); @@ -136,7 +142,7 @@ describe('#installSnapshot()', () => { it('passes through all options+log to installSnapshot()', async () => { installSnapshot.mockResolvedValue({}); - const cluster = new Cluster(log); + const cluster = new Cluster({ log }); await cluster.installSnapshot({ foo: 'bar' }); expect(installSnapshot).toHaveBeenCalledTimes(1); expect(installSnapshot).toHaveBeenCalledWith({ @@ -147,7 +153,7 @@ describe('#installSnapshot()', () => { it('rejects if installSnapshot() rejects', async () => { installSnapshot.mockRejectedValue(new Error('foo')); - const cluster = new Cluster(log); + const cluster = new Cluster({ log }); await expect(cluster.installSnapshot()).rejects.toThrowError('foo'); }); }); @@ -164,7 +170,7 @@ describe('#installArchive(path)', () => { }) ); - const cluster = new Cluster(log); + const cluster = new Cluster({ log }); const promise = cluster.installArchive(); await ensureNoResolve(promise); resolveInstallArchive(); @@ -175,7 +181,7 @@ describe('#installArchive(path)', () => { it('passes through path and all options+log to installArchive()', async () => { installArchive.mockResolvedValue({}); - const cluster = new Cluster(log); + const cluster = new Cluster({ log }); await cluster.installArchive('path', { foo: 'bar' }); expect(installArchive).toHaveBeenCalledTimes(1); expect(installArchive).toHaveBeenCalledWith('path', { @@ -186,7 +192,7 @@ describe('#installArchive(path)', () => { it('rejects if installArchive() rejects', async () => { installArchive.mockRejectedValue(new Error('foo')); - const cluster = new Cluster(log); + const cluster = new Cluster({ log }); await expect(cluster.installArchive()).rejects.toThrowError('foo'); }); }); @@ -195,37 +201,37 @@ describe('#start(installPath)', () => { it('rejects when bin/elasticsearch exists with 0 before starting', async () => { mockEsBin({ exitCode: 0, start: false }); - await expect(new Cluster(log).start()).rejects.toThrowError('ES exited without starting'); + await expect(new Cluster({ log }).start()).rejects.toThrowError('ES exited without starting'); }); it('rejects when bin/elasticsearch exists with 143 before starting', async () => { mockEsBin({ exitCode: 143, start: false }); - await expect(new Cluster(log).start()).rejects.toThrowError('ES exited without starting'); + await expect(new Cluster({ log }).start()).rejects.toThrowError('ES exited without starting'); }); it('rejects when bin/elasticsearch exists with 130 before starting', async () => { mockEsBin({ exitCode: 130, start: false }); - await expect(new Cluster(log).start()).rejects.toThrowError('ES exited without starting'); + await expect(new Cluster({ log }).start()).rejects.toThrowError('ES exited without starting'); }); it('rejects when bin/elasticsearch exists with 1 before starting', async () => { mockEsBin({ exitCode: 1, start: false }); - await expect(new Cluster(log).start()).rejects.toThrowError('ES exited with code 1'); + await expect(new Cluster({ log }).start()).rejects.toThrowError('ES exited with code 1'); }); it('resolves when bin/elasticsearch logs "started"', async () => { mockEsBin({ start: true }); - await new Cluster(log).start(); + await new Cluster({ log }).start(); }); it('rejects if #start() was called previously', async () => { mockEsBin({ start: true }); - const cluster = new Cluster(log); + const cluster = new Cluster({ log }); await cluster.start(); await expect(cluster.start()).rejects.toThrowError('ES has already been started'); }); @@ -233,41 +239,66 @@ describe('#start(installPath)', () => { it('rejects if #run() was called previously', async () => { mockEsBin({ start: true }); - const cluster = new Cluster(log); + const cluster = new Cluster({ log }); await cluster.run(); await expect(cluster.start()).rejects.toThrowError('ES has already been started'); }); + + it('sets up SSL when enabled', async () => { + mockEsBin({ start: true, ssl: true }); + + const cluster = new Cluster({ log, ssl: true }); + await cluster.start(); + + const config = extractConfigFiles.mock.calls[0][0]; + expect(config).toContain('xpack.security.http.ssl.enabled=true'); + expect(config).toContain(`xpack.security.http.ssl.key=${ES_KEY_PATH}`); + expect(config).toContain(`xpack.security.http.ssl.certificate=${ES_CERT_PATH}`); + expect(config).toContain(`xpack.security.http.ssl.certificate_authorities=${CA_CERT_PATH}`); + }); + + it(`doesn't setup SSL when disabled`, async () => { + mockEsBin({ start: true }); + + extractConfigFiles.mockReturnValueOnce([]); + + const cluster = new Cluster({ log, ssl: false }); + await cluster.start(); + + const config = extractConfigFiles.mock.calls[0][0]; + expect(config).toHaveLength(0); + }); }); describe('#run()', () => { it('resolves when bin/elasticsearch exists with 0', async () => { mockEsBin({ exitCode: 0 }); - await new Cluster(log).run(); + await new Cluster({ log }).run(); }); it('resolves when bin/elasticsearch exists with 143', async () => { mockEsBin({ exitCode: 143 }); - await new Cluster(log).run(); + await new Cluster({ log }).run(); }); it('resolves when bin/elasticsearch exists with 130', async () => { mockEsBin({ exitCode: 130 }); - await new Cluster(log).run(); + await new Cluster({ log }).run(); }); it('rejects when bin/elasticsearch exists with 1', async () => { mockEsBin({ exitCode: 1 }); - await expect(new Cluster(log).run()).rejects.toThrowError('ES exited with code 1'); + await expect(new Cluster({ log }).run()).rejects.toThrowError('ES exited with code 1'); }); it('rejects if #start() was called previously', async () => { mockEsBin({ exitCode: 0, start: true }); - const cluster = new Cluster(log); + const cluster = new Cluster({ log }); await cluster.start(); await expect(cluster.run()).rejects.toThrowError('ES has already been started'); }); @@ -275,22 +306,47 @@ describe('#run()', () => { it('rejects if #run() was called previously', async () => { mockEsBin({ exitCode: 0 }); - const cluster = new Cluster(log); + const cluster = new Cluster({ log }); await cluster.run(); await expect(cluster.run()).rejects.toThrowError('ES has already been started'); }); + + it('sets up SSL when enabled', async () => { + mockEsBin({ start: true, ssl: true }); + + const cluster = new Cluster({ log, ssl: true }); + await cluster.run(); + + const config = extractConfigFiles.mock.calls[0][0]; + expect(config).toContain('xpack.security.http.ssl.enabled=true'); + expect(config).toContain(`xpack.security.http.ssl.key=${ES_KEY_PATH}`); + expect(config).toContain(`xpack.security.http.ssl.certificate=${ES_CERT_PATH}`); + expect(config).toContain(`xpack.security.http.ssl.certificate_authorities=${CA_CERT_PATH}`); + }); + + it(`doesn't setup SSL when disabled`, async () => { + mockEsBin({ start: true }); + + extractConfigFiles.mockReturnValueOnce([]); + + const cluster = new Cluster({ log, ssl: false }); + await cluster.run(); + + const config = extractConfigFiles.mock.calls[0][0]; + expect(config).toHaveLength(0); + }); }); describe('#stop()', () => { it('rejects if #run() or #start() was not called', async () => { - const cluster = new Cluster(log); + const cluster = new Cluster({ log }); await expect(cluster.stop()).rejects.toThrowError('ES has not been started'); }); it('resolves when ES exits with 0', async () => { mockEsBin({ exitCode: 0, start: true }); - const cluster = new Cluster(log); + const cluster = new Cluster({ log }); await cluster.start(); await cluster.stop(); }); @@ -298,7 +354,7 @@ describe('#stop()', () => { it('resolves when ES exits with 143', async () => { mockEsBin({ exitCode: 143, start: true }); - const cluster = new Cluster(log); + const cluster = new Cluster({ log }); await cluster.start(); await cluster.stop(); }); @@ -306,7 +362,7 @@ describe('#stop()', () => { it('resolves when ES exits with 130', async () => { mockEsBin({ exitCode: 130, start: true }); - const cluster = new Cluster(log); + const cluster = new Cluster({ log }); await cluster.start(); await cluster.stop(); }); @@ -314,7 +370,7 @@ describe('#stop()', () => { it('rejects when ES exits with 1', async () => { mockEsBin({ exitCode: 1, start: true }); - const cluster = new Cluster(log); + const cluster = new Cluster({ log }); await expect(cluster.run()).rejects.toThrowError('ES exited with code 1'); await expect(cluster.stop()).rejects.toThrowError('ES exited with code 1'); }); diff --git a/packages/kbn-es/src/utils/native_realm.js b/packages/kbn-es/src/utils/native_realm.js index 72bc471fd86f1..247ddc461910f 100644 --- a/packages/kbn-es/src/utils/native_realm.js +++ b/packages/kbn-es/src/utils/native_realm.js @@ -23,8 +23,16 @@ const chalk = require('chalk'); const { log: defaultLog } = require('./log'); exports.NativeRealm = class NativeRealm { - constructor(elasticPassword, port, log = defaultLog) { - this._client = new Client({ node: `http://elastic:${elasticPassword}@localhost:${port}` }); + constructor({ elasticPassword, port, log = defaultLog, ssl = false, caCert }) { + this._client = new Client({ + node: `${ssl ? 'https' : 'http'}://elastic:${elasticPassword}@localhost:${port}`, + ssl: ssl + ? { + ca: caCert, + rejectUnauthorized: true, + } + : undefined, + }); this._elasticPassword = elasticPassword; this._log = log; } diff --git a/packages/kbn-es/src/utils/native_realm.test.js b/packages/kbn-es/src/utils/native_realm.test.js index ae3c615f1ca25..99c7ed1623014 100644 --- a/packages/kbn-es/src/utils/native_realm.test.js +++ b/packages/kbn-es/src/utils/native_realm.test.js @@ -40,7 +40,7 @@ const log = new ToolingLog(); let nativeRealm; beforeEach(() => { - nativeRealm = new NativeRealm('changeme', '9200', log); + nativeRealm = new NativeRealm({ elasticPassword: 'changeme', port: '9200', log }); }); afterAll(() => { diff --git a/packages/kbn-test/src/es/es_test_cluster.js b/packages/kbn-test/src/es/es_test_cluster.js index b0615d30c2c91..896029b08363d 100644 --- a/packages/kbn-test/src/es/es_test_cluster.js +++ b/packages/kbn-test/src/es/es_test_cluster.js @@ -54,7 +54,7 @@ export function createEsTestCluster(options = {}) { esArgs, }; - const cluster = new Cluster(log); + const cluster = new Cluster({ log }); return new (class EsTestCluster { getStartTimeout() { diff --git a/scripts/es.js b/scripts/es.js index bd64857d81ad5..93f1d69350bac 100644 --- a/scripts/es.js +++ b/scripts/es.js @@ -30,6 +30,7 @@ kbnEs version: pkg.version, 'source-path': resolve(__dirname, '../../elasticsearch'), 'base-path': resolve(__dirname, '../.es'), + ssl: false, }) .catch(function (e) { console.error(e);