From da2f92f3df1d43082dd3b6c0b4fa4b88bc41d049 Mon Sep 17 00:00:00 2001 From: Mike Cote Date: Fri, 2 Aug 2019 13:30:16 -0400 Subject: [PATCH] Allow tests to use SSL between Kibana and Elasticsearch --- packages/kbn-test/src/es/es_test_cluster.js | 3 +- .../lib/config/schema.ts | 1 + .../kbn-test/src/functional_tests/lib/auth.js | 59 ++++++++++++++++--- .../functional_tests/lib/run_elasticsearch.js | 20 +++++-- src/test_utils/kbn_server.ts | 18 +++--- x-pack/test/api_integration/config.js | 13 +++- 6 files changed, 92 insertions(+), 22 deletions(-) diff --git a/packages/kbn-test/src/es/es_test_cluster.js b/packages/kbn-test/src/es/es_test_cluster.js index 896029b08363..d1aa25591446 100644 --- a/packages/kbn-test/src/es/es_test_cluster.js +++ b/packages/kbn-test/src/es/es_test_cluster.js @@ -38,6 +38,7 @@ export function createEsTestCluster(options = {}) { esFrom = esTestConfig.getBuildFrom(), dataArchive, esArgs, + ssl, } = options; const randomHash = Math.random() @@ -54,7 +55,7 @@ export function createEsTestCluster(options = {}) { esArgs, }; - const cluster = new Cluster({ log }); + const cluster = new Cluster({ log, ssl }); return new (class EsTestCluster { getStartTimeout() { diff --git a/packages/kbn-test/src/functional_test_runner/lib/config/schema.ts b/packages/kbn-test/src/functional_test_runner/lib/config/schema.ts index 37bfbae2912f..4887ad2c6e1d 100644 --- a/packages/kbn-test/src/functional_test_runner/lib/config/schema.ts +++ b/packages/kbn-test/src/functional_test_runner/lib/config/schema.ts @@ -178,6 +178,7 @@ export const schema = Joi.object() serverArgs: Joi.array(), serverEnvVars: Joi.object(), dataArchive: Joi.string(), + ssl: Joi.boolean().default(false), }) .default(), diff --git a/packages/kbn-test/src/functional_tests/lib/auth.js b/packages/kbn-test/src/functional_tests/lib/auth.js index b696ab8f4c12..358b16c562b1 100644 --- a/packages/kbn-test/src/functional_tests/lib/auth.js +++ b/packages/kbn-test/src/functional_tests/lib/auth.js @@ -17,6 +17,8 @@ * under the License. */ +import fs from 'fs'; +import util from 'util'; import { format as formatUrl } from 'url'; import request from 'request'; @@ -24,13 +26,23 @@ import { delay } from 'bluebird'; export const DEFAULT_SUPERUSER_PASS = 'changeme'; -async function updateCredentials(port, auth, username, password, retries = 10) { +const readFile = util.promisify(fs.readFile); + +async function updateCredentials({ + port, + auth, + username, + password, + retries = 10, + protocol, + caCert, +}) { const result = await new Promise((resolve, reject) => request( { method: 'PUT', uri: formatUrl({ - protocol: 'http:', + protocol: `${protocol}:`, auth, hostname: 'localhost', port, @@ -38,6 +50,7 @@ async function updateCredentials(port, auth, username, password, retries = 10) { }), json: true, body: { password }, + ca: caCert, }, (err, httpResponse, body) => { if (err) return reject(err); @@ -55,26 +68,35 @@ async function updateCredentials(port, auth, username, password, retries = 10) { if (retries > 0) { await delay(2500); - return await updateCredentials(port, auth, username, password, retries - 1); + return await updateCredentials({ + port, + auth, + username, + password, + retries: retries - 1, + protocol, + caCert, + }); } throw new Error(`${statusCode} response, expected 200 -- ${JSON.stringify(body)}`); } -export async function setupUsers(log, esPort, updates) { +export async function setupUsers({ log, esPort, updates, protocol = 'http', caPath }) { // track the current credentials for the `elastic` user as // they will likely change as we apply updates let auth = `elastic:${DEFAULT_SUPERUSER_PASS}`; + const caCert = caPath && (await readFile(caPath)); for (const { username, password, roles } of updates) { // If working with a built-in user, just change the password if (['logstash_system', 'elastic', 'kibana'].includes(username)) { - await updateCredentials(esPort, auth, username, password); + await updateCredentials({ port: esPort, auth, username, password, protocol, caCert }); log.info('setting %j user password to %j', username, password); // If not a builtin user, add them } else { - await insertUser(esPort, auth, username, password, roles); + await insertUser({ port: esPort, auth, username, password, roles, protocol, caCert }); log.info('Added %j user with password to %j', username, password); } @@ -84,13 +106,22 @@ export async function setupUsers(log, esPort, updates) { } } -async function insertUser(port, auth, username, password, roles = [], retries = 10) { +async function insertUser({ + port, + auth, + username, + password, + roles = [], + retries = 10, + protocol, + caCert, +}) { const result = await new Promise((resolve, reject) => request( { method: 'POST', uri: formatUrl({ - protocol: 'http:', + protocol: `${protocol}:`, auth, hostname: 'localhost', port, @@ -98,6 +129,7 @@ async function insertUser(port, auth, username, password, roles = [], retries = }), json: true, body: { password, roles }, + ca: caCert, }, (err, httpResponse, body) => { if (err) return reject(err); @@ -114,7 +146,16 @@ async function insertUser(port, auth, username, password, roles = [], retries = if (retries > 0) { await delay(2500); - return await insertUser(port, auth, username, password, retries - 1); + return await insertUser({ + port, + auth, + username, + password, + roles, + retries: retries - 1, + protocol, + caCert, + }); } throw new Error(`${statusCode} response, expected 200 -- ${JSON.stringify(body)}`); diff --git a/packages/kbn-test/src/functional_tests/lib/run_elasticsearch.js b/packages/kbn-test/src/functional_tests/lib/run_elasticsearch.js index c049782c4d87..2423665bacb8 100644 --- a/packages/kbn-test/src/functional_tests/lib/run_elasticsearch.js +++ b/packages/kbn-test/src/functional_tests/lib/run_elasticsearch.js @@ -25,6 +25,7 @@ import { setupUsers, DEFAULT_SUPERUSER_PASS } from './auth'; export async function runElasticsearch({ config, options }) { const { log, esFrom } = options; + const ssl = config.get('esTestCluster.ssl'); const license = config.get('esTestCluster.license'); const esArgs = config.get('esTestCluster.serverArgs'); const esEnvVars = config.get('esTestCluster.serverEnvVars'); @@ -41,16 +42,27 @@ export async function runElasticsearch({ config, options }) { esFrom: esFrom || config.get('esTestCluster.from'), dataArchive: config.get('esTestCluster.dataArchive'), esArgs, + ssl, }); await cluster.start(esArgs, esEnvVars); if (isSecurityEnabled) { - await setupUsers(log, config.get('servers.elasticsearch.port'), [ - config.get('servers.elasticsearch'), - config.get('servers.kibana'), - ]); + await setupUsers({ + log, + esPort: config.get('servers.elasticsearch.port'), + updates: [config.get('servers.elasticsearch'), config.get('servers.kibana')], + protocol: config.get('servers.elasticsearch').protocol, + caPath: getRelativeCertificateAuthorityPath(config.get('kbnTestServer.serverArgs')), + }); } return cluster; } + +function getRelativeCertificateAuthorityPath(esConfig = []) { + const caConfig = esConfig.find( + config => config.indexOf('--elasticsearch.ssl.certificateAuthorities') === 0 + ); + return caConfig ? caConfig.split('=')[1] : undefined; +} diff --git a/src/test_utils/kbn_server.ts b/src/test_utils/kbn_server.ts index f14b0b5fb2d4..706a8050a0ad 100644 --- a/src/test_utils/kbn_server.ts +++ b/src/test_utils/kbn_server.ts @@ -221,13 +221,17 @@ export function createTestServers({ await es.start(); if (['gold', 'trial'].includes(license)) { - await setupUsers(log, esTestConfig.getUrlParts().port, [ - ...usersToBeAdded, - // user elastic - esTestConfig.getUrlParts(), - // user kibana - kbnTestConfig.getUrlParts(), - ]); + await setupUsers({ + log, + esPort: esTestConfig.getUrlParts().port, + updates: [ + ...usersToBeAdded, + // user elastic + esTestConfig.getUrlParts(), + // user kibana + kbnTestConfig.getUrlParts(), + ], + }); // Override provided configs, we know what the elastic user is now kbnSettings.elasticsearch = { diff --git a/x-pack/test/api_integration/config.js b/x-pack/test/api_integration/config.js index cf11f6953b58..70e6dde75234 100644 --- a/x-pack/test/api_integration/config.js +++ b/x-pack/test/api_integration/config.js @@ -12,10 +12,18 @@ import { SLACK_ACTION_SIMULATOR_URI } from './fixtures/plugins/actions'; export async function getApiIntegrationConfig({ readConfigFile }) { const xPackFunctionalTestsConfig = await readConfigFile(require.resolve('../functional/config.js')); + const servers = { + ...xPackFunctionalTestsConfig.get('servers'), + elasticsearch: { + ...xPackFunctionalTestsConfig.get('servers').elasticsearch, + protocol: 'https', + }, + }; + return { testFiles: [require.resolve('./apis')], services, - servers: xPackFunctionalTestsConfig.get('servers'), + servers, esArchiver: xPackFunctionalTestsConfig.get('esArchiver'), junit: { reportName: 'X-Pack API Integration Tests', @@ -28,10 +36,13 @@ export async function getApiIntegrationConfig({ readConfigFile }) { `--plugin-path=${path.join(__dirname, 'fixtures', 'plugins', 'alerts')}`, `--plugin-path=${path.join(__dirname, 'fixtures', 'plugins', 'actions')}`, `--server.xsrf.whitelist=${JSON.stringify([SLACK_ACTION_SIMULATOR_URI])}`, + `--elasticsearch.hosts=${servers.elasticsearch.protocol}://${servers.elasticsearch.hostname}:${servers.elasticsearch.port}`, + `--elasticsearch.ssl.certificateAuthorities=${path.resolve(__dirname, '../../../test/dev_certs/ca.crt')}`, ], }, esTestCluster: { ...xPackFunctionalTestsConfig.get('esTestCluster'), + ssl: true, serverArgs: [ ...xPackFunctionalTestsConfig.get('esTestCluster.serverArgs'), 'node.attr.name=apiIntegrationTestNode'