From 270a7af04cef9ccf3b0f8b59c7fbad856eff888e Mon Sep 17 00:00:00 2001 From: kobelb Date: Fri, 19 Oct 2018 15:06:05 -0700 Subject: [PATCH 1/4] Adding option to always present the certificate when connecting to ES --- .../__tests__/elasticsearch_proxy_config.js | 60 ++++++++++++++----- .../server/elasticsearch_proxy_config.js | 10 ++++ src/core_plugins/elasticsearch/index.js | 3 +- src/core_plugins/elasticsearch/lib/cluster.js | 5 +- .../elasticsearch/lib/create_agent.js | 3 +- 5 files changed, 64 insertions(+), 17 deletions(-) diff --git a/src/core_plugins/console/server/__tests__/elasticsearch_proxy_config.js b/src/core_plugins/console/server/__tests__/elasticsearch_proxy_config.js index 3a721bb18dfac..74bf9fee2793b 100644 --- a/src/core_plugins/console/server/__tests__/elasticsearch_proxy_config.js +++ b/src/core_plugins/console/server/__tests__/elasticsearch_proxy_config.js @@ -18,11 +18,15 @@ */ import expect from 'expect.js'; +import fs from 'fs'; +import { promisify } from 'bluebird'; import { getElasticsearchProxyConfig } from '../elasticsearch_proxy_config'; import https from 'https'; import http from 'http'; import sinon from 'sinon'; +const readFileAsync = promisify(fs.readFile, fs); + describe('plugins/console', function () { describe('#getElasticsearchProxyConfig', function () { @@ -118,22 +122,50 @@ describe('plugins/console', function () { expect(agent.options.ca).to.contain('test ca certificate\n'); }); - it(`doesn't set cert and key when certificate and key paths are specified`, function () { - setElasticsearchConfig('ssl.certificate', __dirname + '/fixtures/cert.crt'); - setElasticsearchConfig('ssl.key', __dirname + '/fixtures/cert.key'); - - const { agent } = getElasticsearchProxyConfig(server); - expect(agent.options.cert).to.be(undefined); - expect(agent.options.key).to.be(undefined); + describe('when alwaysPresentCertificate is false', () => { + it(`doesn't set cert and key when certificate and key paths are specified`, function () { + setElasticsearchConfig('ssl.alwaysPresentCertificate', false); + setElasticsearchConfig('ssl.certificate', __dirname + '/fixtures/cert.crt'); + setElasticsearchConfig('ssl.key', __dirname + '/fixtures/cert.key'); + + const { agent } = getElasticsearchProxyConfig(server); + expect(agent.options.cert).to.be(undefined); + expect(agent.options.key).to.be(undefined); + }); + + it(`doesn't set passphrase when certificate, key and keyPassphrase are specified`, function () { + setElasticsearchConfig('ssl.alwaysPresentCertificate', false); + setElasticsearchConfig('ssl.certificate', __dirname + '/fixtures/cert.crt'); + setElasticsearchConfig('ssl.key', __dirname + '/fixtures/cert.key'); + setElasticsearchConfig('ssl.keyPassphrase', 'secret'); + + const { agent } = getElasticsearchProxyConfig(server); + expect(agent.options.passphrase).to.be(undefined); + }); }); - it(`doesn't set passphrase when certificate, key and keyPassphrase are specified`, function () { - setElasticsearchConfig('ssl.certificate', __dirname + '/fixtures/cert.crt'); - setElasticsearchConfig('ssl.key', __dirname + '/fixtures/cert.key'); - setElasticsearchConfig('ssl.keyPassphrase', 'secret'); - - const { agent } = getElasticsearchProxyConfig(server); - expect(agent.options.passphrase).to.be(undefined); + describe('when alwaysPresentCertificate is true', () => { + it(`sets cert and key when certificate and key paths are specified`, async function () { + const certificatePath = __dirname + '/fixtures/cert.crt'; + const keyPath = __dirname + '/fixtures/cert.key'; + setElasticsearchConfig('ssl.alwaysPresentCertificate', true); + setElasticsearchConfig('ssl.certificate', certificatePath); + setElasticsearchConfig('ssl.key', keyPath); + + const { agent } = getElasticsearchProxyConfig(server); + expect(agent.options.cert).to.be(await readFileAsync(certificatePath, 'utf8')); + expect(agent.options.key).to.be(await readFileAsync(keyPath, 'utf8')); + }); + + it(`sets passphrase when certificate, key and keyPassphrase are specified`, function () { + setElasticsearchConfig('ssl.alwaysPresentCertificate', true); + setElasticsearchConfig('ssl.certificate', __dirname + '/fixtures/cert.crt'); + setElasticsearchConfig('ssl.key', __dirname + '/fixtures/cert.key'); + setElasticsearchConfig('ssl.keyPassphrase', 'secret'); + + const { agent } = getElasticsearchProxyConfig(server); + expect(agent.options.passphrase).to.be('secret'); + }); }); }); }); diff --git a/src/core_plugins/console/server/elasticsearch_proxy_config.js b/src/core_plugins/console/server/elasticsearch_proxy_config.js index 86fb37eb86eb7..b1ece31d9e3d9 100644 --- a/src/core_plugins/console/server/elasticsearch_proxy_config.js +++ b/src/core_plugins/console/server/elasticsearch_proxy_config.js @@ -55,6 +55,16 @@ const createAgent = (server) => { agentOptions.ca = config.get('elasticsearch.ssl.certificateAuthorities').map(readFile); } + if ( + config.get('elasticsearch.ssl.alwaysPresentCertificate') && + config.get('elasticsearch.ssl.certificate') && + config.get('elasticsearch.ssl.key') + ) { + agentOptions.cert = readFile(config.get('elasticsearch.ssl.certificate')); + agentOptions.key = readFile(config.get('elasticsearch.ssl.key')); + agentOptions.passphrase = config.get('elasticsearch.ssl.keyPassphrase'); + } + return new https.Agent(agentOptions); }; diff --git a/src/core_plugins/elasticsearch/index.js b/src/core_plugins/elasticsearch/index.js index 87af4ace6a668..2b5df76d743fb 100644 --- a/src/core_plugins/elasticsearch/index.js +++ b/src/core_plugins/elasticsearch/index.js @@ -42,7 +42,8 @@ export default function (kibana) { certificateAuthorities: array().single().items(string()), certificate: string(), key: string(), - keyPassphrase: string() + keyPassphrase: string(), + alwaysPresentCertificate: boolean().default(false), }).default(); return object({ diff --git a/src/core_plugins/elasticsearch/lib/cluster.js b/src/core_plugins/elasticsearch/lib/cluster.js index 52644e5a8b3e2..be6399257cd10 100644 --- a/src/core_plugins/elasticsearch/lib/cluster.js +++ b/src/core_plugins/elasticsearch/lib/cluster.js @@ -34,7 +34,10 @@ export class Cluster { this._clients = new Set(); this._client = this.createClient(); - this._noAuthClient = this.createClient({ auth: false }, { ignoreCertAndKey: true }); + this._noAuthClient = this.createClient( + { auth: false }, + { ignoreCertAndKey: !this.getSsl().alwaysPresentCertificate } + ); return this; } diff --git a/src/core_plugins/elasticsearch/lib/create_agent.js b/src/core_plugins/elasticsearch/lib/create_agent.js index 6bce56b27032b..f636c9edcc1b8 100644 --- a/src/core_plugins/elasticsearch/lib/create_agent.js +++ b/src/core_plugins/elasticsearch/lib/create_agent.js @@ -29,5 +29,6 @@ export default function (config) { if (!/^https/.test(target.protocol)) return new http.Agent(); - return new https.Agent(parseConfig(config, { ignoreCertAndKey: true }).ssl); + const ignoreCertAndKey = !get(config, 'ssl.alwaysPresentCertificate'); + return new https.Agent(parseConfig(config, { ignoreCertAndKey }).ssl); } From 527b36807dee08d458db216c9474dd13222d01f6 Mon Sep 17 00:00:00 2001 From: kobelb Date: Fri, 19 Oct 2018 15:18:27 -0700 Subject: [PATCH 2/4] Updating docs --- docs/setup/settings.asciidoc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/setup/settings.asciidoc b/docs/setup/settings.asciidoc index 55128f213f356..4303e739d73fa 100644 --- a/docs/setup/settings.asciidoc +++ b/docs/setup/settings.asciidoc @@ -49,6 +49,11 @@ certificate and key files. These files are used to verify the identity of Kibana `elasticsearch.ssl.verificationMode:`:: *Default: full* Controls the verification of certificates presented by Elasticsearch. Valid values are `none`, `certificate`, and `full`. `full` performs hostname verification, and `certificate` does not. +`elasticsearch.ssl.alwaysPresentCertificate:`:: *Default: false* Controls whether to always present the certificate specified +by `elasticsearch.ssl.certificate` when requested. This applies to all requests to Elasticsearch, including requests that are +proxied for end-users. Setting this to `true` when Elasticsearch is using certificates to authenticate users can lead to proxied +requests for end-users being executed as the identity tied to the configured certificate. + `elasticsearch.startupTimeout:`:: *Default: 5000* Time in milliseconds to wait for Elasticsearch at Kibana startup before retrying. From 5f6249c1cbab5866c725b52e095653861907daa7 Mon Sep 17 00:00:00 2001 From: kobelb Date: Mon, 22 Oct 2018 09:46:41 -0700 Subject: [PATCH 3/4] Adding some more tests --- .../__tests__/elasticsearch_proxy_config.js | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/core_plugins/console/server/__tests__/elasticsearch_proxy_config.js b/src/core_plugins/console/server/__tests__/elasticsearch_proxy_config.js index 74bf9fee2793b..79821f6ff54e7 100644 --- a/src/core_plugins/console/server/__tests__/elasticsearch_proxy_config.js +++ b/src/core_plugins/console/server/__tests__/elasticsearch_proxy_config.js @@ -166,6 +166,29 @@ describe('plugins/console', function () { const { agent } = getElasticsearchProxyConfig(server); expect(agent.options.passphrase).to.be('secret'); }); + + it(`doesn't set cert when only certificate path is specified`, async function () { + const certificatePath = __dirname + '/fixtures/cert.crt'; + setElasticsearchConfig('ssl.alwaysPresentCertificate', true); + setElasticsearchConfig('ssl.certificate', certificatePath); + setElasticsearchConfig('ssl.key', undefined); + + const { agent } = getElasticsearchProxyConfig(server); + expect(agent.options.cert).to.be(undefined); + expect(agent.options.key).to.be(undefined); + }); + + it(`doesn't set key when only key path is specified`, async function () { + const keyPath = __dirname + '/fixtures/cert.key'; + setElasticsearchConfig('ssl.alwaysPresentCertificate', true); + setElasticsearchConfig('ssl.certificate', undefined); + setElasticsearchConfig('ssl.key', keyPath); + + const { agent } = getElasticsearchProxyConfig(server); + expect(agent.options.cert).to.be(undefined); + expect(agent.options.key).to.be(undefined); + }); + }); }); }); From 946d30851609e286d90ef11d2836c1c1abba7619 Mon Sep 17 00:00:00 2001 From: kobelb Date: Mon, 22 Oct 2018 12:05:13 -0700 Subject: [PATCH 4/4] Adding alwaysPresentCertificate option to monitoring --- x-pack/plugins/monitoring/config.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/monitoring/config.js b/x-pack/plugins/monitoring/config.js index 431aed95182fb..3341805910356 100644 --- a/x-pack/plugins/monitoring/config.js +++ b/x-pack/plugins/monitoring/config.js @@ -76,7 +76,8 @@ export const config = (Joi) => { certificateAuthorities: array().single().items(string()), certificate: string(), key: string(), - keyPassphrase: string() + keyPassphrase: string(), + alwaysPresentCertificate: boolean().default(false), }).default(), apiVersion: string().default('master') }).default()