From 9b268cfaddcc89289515312777dd88e5110facb6 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 13 Apr 2016 07:18:39 -0700 Subject: [PATCH 01/18] Filter out origin header before proxying request to ES server upstream --- .../elasticsearch/lib/__tests__/map_uri.js | 41 +++++++++++++++++++ src/plugins/elasticsearch/lib/map_uri.js | 11 ++++- 2 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 src/plugins/elasticsearch/lib/__tests__/map_uri.js diff --git a/src/plugins/elasticsearch/lib/__tests__/map_uri.js b/src/plugins/elasticsearch/lib/__tests__/map_uri.js new file mode 100644 index 0000000000000..b3ab687d05470 --- /dev/null +++ b/src/plugins/elasticsearch/lib/__tests__/map_uri.js @@ -0,0 +1,41 @@ +import expect from 'expect.js'; +import mapUri from '../map_uri'; + +describe('plugins/elasticsearch', function () { + describe('lib/map_uri', function () { + + let server; + let request; + + beforeEach(function () { + server = { + config() { + return { + get() { + return 'http://foobar:9200'; + } + }; + } + }; + + request = { + path: '/elasticsearch/some/path', + headers: { + cookie: 'some_cookie_string', + 'accept-encoding': 'gzip, deflate', + origin: 'https://localhost:5601', + 'content-type': 'application/json', + accept: 'application/json, text/plain, */*' + } + }; + }); + + it ('filters out the origin header from the client', function () { + mapUri(server)(request, function (err, upstreamUri, upstreamHeaders) { + expect(err).to.be(null); + expect(upstreamHeaders).not.to.have.property('origin'); + expect(Object.keys(upstreamHeaders).length).to.be(4); + }); + }); + }); +}); diff --git a/src/plugins/elasticsearch/lib/map_uri.js b/src/plugins/elasticsearch/lib/map_uri.js index 79a2357f07086..31f75254ab03b 100644 --- a/src/plugins/elasticsearch/lib/map_uri.js +++ b/src/plugins/elasticsearch/lib/map_uri.js @@ -1,5 +1,14 @@ import querystring from 'querystring'; import { resolve } from 'url'; +import _ from 'lodash'; + +const filterHeaders = function (originalHeaders) { + const headersToRemove = [ + 'origin' + ]; + return _.omit(originalHeaders, headersToRemove); +}; + module.exports = function mapUri(server, prefix) { const config = server.config(); return function (request, done) { @@ -11,6 +20,6 @@ module.exports = function mapUri(server, prefix) { } const query = querystring.stringify(request.query); if (query) url += '?' + query; - done(null, url); + done(null, url, filterHeaders(request.headers)); }; }; From 1ed0a2516f49313f590ee5eb1ca39e93101554f4 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 13 Apr 2016 07:19:10 -0700 Subject: [PATCH 02/18] Disable automatic passthrough of request/response headers to/from upstream --- src/plugins/elasticsearch/lib/create_proxy.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/plugins/elasticsearch/lib/create_proxy.js b/src/plugins/elasticsearch/lib/create_proxy.js index b2a82a07a1bca..2ef2bd1da56d8 100644 --- a/src/plugins/elasticsearch/lib/create_proxy.js +++ b/src/plugins/elasticsearch/lib/create_proxy.js @@ -16,10 +16,12 @@ function createProxy(server, method, route, config) { handler: { proxy: { mapUri: mapUri(server), - passThrough: true, agent: createAgent(server), xforward: true, - timeout: server.config().get('elasticsearch.requestTimeout') + timeout: server.config().get('elasticsearch.requestTimeout'), + onResponse: function (err, responseFromUpstream, request, reply) { + reply(err, responseFromUpstream); + } } }, }; From b102e26ac314f3002386ece7ed9de5072bffc44c Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 13 Apr 2016 16:50:18 -0700 Subject: [PATCH 03/18] Filter headers using whitelist, not blacklist --- src/plugins/elasticsearch/index.js | 1 + .../elasticsearch/lib/__tests__/map_uri.js | 24 ++++++++++--------- src/plugins/elasticsearch/lib/map_uri.js | 17 +++++++------ 3 files changed, 24 insertions(+), 18 deletions(-) diff --git a/src/plugins/elasticsearch/index.js b/src/plugins/elasticsearch/index.js index e41c5113af600..ad6405c37bcb0 100644 --- a/src/plugins/elasticsearch/index.js +++ b/src/plugins/elasticsearch/index.js @@ -20,6 +20,7 @@ module.exports = function ({ Plugin }) { password: string(), shardTimeout: number().default(0), requestTimeout: number().default(30000), + requestHeaders: array().items(string()).single().default([]), pingTimeout: number().default(ref('requestTimeout')), startupTimeout: number().default(5000), ssl: object({ diff --git a/src/plugins/elasticsearch/lib/__tests__/map_uri.js b/src/plugins/elasticsearch/lib/__tests__/map_uri.js index b3ab687d05470..93be504307e99 100644 --- a/src/plugins/elasticsearch/lib/__tests__/map_uri.js +++ b/src/plugins/elasticsearch/lib/__tests__/map_uri.js @@ -1,5 +1,6 @@ import expect from 'expect.js'; import mapUri from '../map_uri'; +import sinon from 'sinon'; describe('plugins/elasticsearch', function () { describe('lib/map_uri', function () { @@ -8,14 +9,12 @@ describe('plugins/elasticsearch', function () { let request; beforeEach(function () { + const get = sinon.stub() + .withArgs('elasticsearch.url').returns('http://foobar:9200') + .withArgs('elasticsearch.requestHeaders').returns(['x-my-custom-HEADER', 'Authorization']); + const config = function () { return { get: get }; }; server = { - config() { - return { - get() { - return 'http://foobar:9200'; - } - }; - } + config: config }; request = { @@ -25,16 +24,19 @@ describe('plugins/elasticsearch', function () { 'accept-encoding': 'gzip, deflate', origin: 'https://localhost:5601', 'content-type': 'application/json', - accept: 'application/json, text/plain, */*' + 'x-my-custom-header': '42', + accept: 'application/json, text/plain, */*', + authorization: '2343d322eda344390fdw42' } }; }); - it ('filters out the origin header from the client', function () { + it ('only keeps the whitelisted request headers', function () { mapUri(server)(request, function (err, upstreamUri, upstreamHeaders) { expect(err).to.be(null); - expect(upstreamHeaders).not.to.have.property('origin'); - expect(Object.keys(upstreamHeaders).length).to.be(4); + expect(upstreamHeaders).to.have.property('authorization'); + expect(upstreamHeaders).to.have.property('x-my-custom-header'); + expect(Object.keys(upstreamHeaders).length).to.be(2); }); }); }); diff --git a/src/plugins/elasticsearch/lib/map_uri.js b/src/plugins/elasticsearch/lib/map_uri.js index 31f75254ab03b..0d0e04fb6536b 100644 --- a/src/plugins/elasticsearch/lib/map_uri.js +++ b/src/plugins/elasticsearch/lib/map_uri.js @@ -2,14 +2,17 @@ import querystring from 'querystring'; import { resolve } from 'url'; import _ from 'lodash'; -const filterHeaders = function (originalHeaders) { - const headersToRemove = [ - 'origin' - ]; - return _.omit(originalHeaders, headersToRemove); -}; - module.exports = function mapUri(server, prefix) { + + const filterHeaders = function (originalHeaders) { + const headersToKeep = server.config().get('elasticsearch.requestHeaders'); + const headersToKeepNormalized = headersToKeep.map(function (header) { + return header.trim().toLowerCase(); + }); + + return _.pick(originalHeaders, headersToKeepNormalized); + }; + const config = server.config(); return function (request, done) { const path = request.path.replace('/elasticsearch', ''); From b635b2c4661a5f6e524e67d2669c55dff225c44b Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 13 Apr 2016 16:56:26 -0700 Subject: [PATCH 04/18] Adding elasticsearch.requestHeaders option to kibana.yml and documentation --- config/kibana.yml | 3 +++ docs/kibana-yml.asciidoc | 3 ++- docs/settings.asciidoc | 4 ++++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/config/kibana.yml b/config/kibana.yml index 0e122d193dcf7..d80d0e34e3306 100644 --- a/config/kibana.yml +++ b/config/kibana.yml @@ -61,6 +61,9 @@ # must be a positive integer. # elasticsearch.requestTimeout: 30000 +# List of Kibana client-side headers to send to Elasticsearch +# elasticsearch.requestHeaders: [ ] + # Time in milliseconds for Elasticsearch to wait for responses from shards. Set to 0 to disable. # elasticsearch.shardTimeout: 0 diff --git a/docs/kibana-yml.asciidoc b/docs/kibana-yml.asciidoc index fa4baf0ae4548..49bc93656c560 100644 --- a/docs/kibana-yml.asciidoc +++ b/docs/kibana-yml.asciidoc @@ -29,7 +29,8 @@ to `false`. wait for Elasticsearch to respond to pings. `elasticsearch.requestTimeout:`:: *Default: 300000* Time in milliseconds to wait for responses from the back end or Elasticsearch. This value must be a positive integer. -`elasticsearch.shardTimeout:`:: *Default: 0* Time in milliseconds for Elasticsearch to wait for responses from shards. Set +`elasticsearch.requestHeaders:`:: *Default: `[]`* List of Kibana client-side headers to send to Elasticsearch. +`elasticsearch.shardTimeout:`:: *Default: 0* Time in milliseconds for Elasticsearch to wait for responses from shards. Set to 0 to disable. `elasticsearch.startupTimeout:`:: *Default: 5000* Time in milliseconds to wait for Elasticsearch at Kibana startup before retrying. diff --git a/docs/settings.asciidoc b/docs/settings.asciidoc index 0f105dc33e06d..ea19d48b52892 100644 --- a/docs/settings.asciidoc +++ b/docs/settings.asciidoc @@ -376,6 +376,10 @@ deprecated[4.2, The names of several Kibana server properties changed in the 4.2 + *default*: `500000` +`elasticsearch.requestHeaders:` added[5.0]:: List of Kibana client-side headers to send to Elasticsearch. ++ +*default*: `[]` + `elasticsearch.shardTimeout` added[4.2]:: How long Elasticsearch should wait for responses from shards. Set to 0 to disable. + *alias*: `shard_timeout` deprecated[4.2] From e56e98f5b8ef8475cd299875c98ac7dc8a33830f Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 13 Apr 2016 17:09:53 -0700 Subject: [PATCH 05/18] Do not send x-forwarded-* headers --- src/plugins/elasticsearch/lib/create_proxy.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/plugins/elasticsearch/lib/create_proxy.js b/src/plugins/elasticsearch/lib/create_proxy.js index 2ef2bd1da56d8..8b1acb9ddb0c9 100644 --- a/src/plugins/elasticsearch/lib/create_proxy.js +++ b/src/plugins/elasticsearch/lib/create_proxy.js @@ -17,7 +17,6 @@ function createProxy(server, method, route, config) { proxy: { mapUri: mapUri(server), agent: createAgent(server), - xforward: true, timeout: server.config().get('elasticsearch.requestTimeout'), onResponse: function (err, responseFromUpstream, request, reply) { reply(err, responseFromUpstream); From ad5d772c3266ed8ff12aa907ca827c116e7a201c Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 13 Apr 2016 18:26:35 -0700 Subject: [PATCH 06/18] Reformatting --- src/plugins/elasticsearch/lib/__tests__/map_uri.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/elasticsearch/lib/__tests__/map_uri.js b/src/plugins/elasticsearch/lib/__tests__/map_uri.js index 93be504307e99..ef9b0cf73356f 100644 --- a/src/plugins/elasticsearch/lib/__tests__/map_uri.js +++ b/src/plugins/elasticsearch/lib/__tests__/map_uri.js @@ -31,7 +31,7 @@ describe('plugins/elasticsearch', function () { }; }); - it ('only keeps the whitelisted request headers', function () { + it('only keeps the whitelisted request headers', function () { mapUri(server)(request, function (err, upstreamUri, upstreamHeaders) { expect(err).to.be(null); expect(upstreamHeaders).to.have.property('authorization'); From 5cca6f71a76f44ee739180f7b4ec8c42c2b56ded Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 13 Apr 2016 18:26:54 -0700 Subject: [PATCH 07/18] Normalizing original request headers as well, just in case --- src/plugins/elasticsearch/lib/map_uri.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/plugins/elasticsearch/lib/map_uri.js b/src/plugins/elasticsearch/lib/map_uri.js index 0d0e04fb6536b..c4afe213daaf6 100644 --- a/src/plugins/elasticsearch/lib/map_uri.js +++ b/src/plugins/elasticsearch/lib/map_uri.js @@ -4,11 +4,14 @@ import _ from 'lodash'; module.exports = function mapUri(server, prefix) { + const normalizeHeader = function (header) { + return header.trim().toLowerCase(); + }; + const filterHeaders = function (originalHeaders) { + const originalHeadersNormalized = _.mapKeys(originalHeaders, _.rearg(normalizeHeader, 1, 0)); const headersToKeep = server.config().get('elasticsearch.requestHeaders'); - const headersToKeepNormalized = headersToKeep.map(function (header) { - return header.trim().toLowerCase(); - }); + const headersToKeepNormalized = headersToKeep.map(normalizeHeader); return _.pick(originalHeaders, headersToKeepNormalized); }; From 5f34d6885ab69dc965a5a182c72210184f761b12 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Thu, 14 Apr 2016 06:15:49 -0700 Subject: [PATCH 08/18] Replacing use of _.rearg with more readable code (IMO) --- src/plugins/elasticsearch/lib/map_uri.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/plugins/elasticsearch/lib/map_uri.js b/src/plugins/elasticsearch/lib/map_uri.js index c4afe213daaf6..5f68ec6cdf05a 100644 --- a/src/plugins/elasticsearch/lib/map_uri.js +++ b/src/plugins/elasticsearch/lib/map_uri.js @@ -9,7 +9,9 @@ module.exports = function mapUri(server, prefix) { }; const filterHeaders = function (originalHeaders) { - const originalHeadersNormalized = _.mapKeys(originalHeaders, _.rearg(normalizeHeader, 1, 0)); + const originalHeadersNormalized = _.mapKeys(originalHeaders, function (headerValue, headerName) { + return normalizeHeader(headerName); + }); const headersToKeep = server.config().get('elasticsearch.requestHeaders'); const headersToKeepNormalized = headersToKeep.map(normalizeHeader); From 836c740e5621c99ec1dae0dd7083583227bea719 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Thu, 14 Apr 2016 16:35:01 -0700 Subject: [PATCH 09/18] Adding 'authorization' to the default headers list --- config/kibana.yml | 2 +- docs/kibana-yml.asciidoc | 2 +- docs/settings.asciidoc | 2 +- src/plugins/elasticsearch/index.js | 4 +++- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/config/kibana.yml b/config/kibana.yml index d80d0e34e3306..44c822328dab1 100644 --- a/config/kibana.yml +++ b/config/kibana.yml @@ -62,7 +62,7 @@ # elasticsearch.requestTimeout: 30000 # List of Kibana client-side headers to send to Elasticsearch -# elasticsearch.requestHeaders: [ ] +# elasticsearch.requestHeaders: [ authorization ] # Time in milliseconds for Elasticsearch to wait for responses from shards. Set to 0 to disable. # elasticsearch.shardTimeout: 0 diff --git a/docs/kibana-yml.asciidoc b/docs/kibana-yml.asciidoc index 49bc93656c560..b42dcc88eeb44 100644 --- a/docs/kibana-yml.asciidoc +++ b/docs/kibana-yml.asciidoc @@ -29,7 +29,7 @@ to `false`. wait for Elasticsearch to respond to pings. `elasticsearch.requestTimeout:`:: *Default: 300000* Time in milliseconds to wait for responses from the back end or Elasticsearch. This value must be a positive integer. -`elasticsearch.requestHeaders:`:: *Default: `[]`* List of Kibana client-side headers to send to Elasticsearch. +`elasticsearch.requestHeaders:`:: *Default: `[ 'authorization' ]`* List of Kibana client-side headers to send to Elasticsearch. `elasticsearch.shardTimeout:`:: *Default: 0* Time in milliseconds for Elasticsearch to wait for responses from shards. Set to 0 to disable. `elasticsearch.startupTimeout:`:: *Default: 5000* Time in milliseconds to wait for Elasticsearch at Kibana startup before diff --git a/docs/settings.asciidoc b/docs/settings.asciidoc index ea19d48b52892..d871abcc92c38 100644 --- a/docs/settings.asciidoc +++ b/docs/settings.asciidoc @@ -378,7 +378,7 @@ deprecated[4.2, The names of several Kibana server properties changed in the 4.2 `elasticsearch.requestHeaders:` added[5.0]:: List of Kibana client-side headers to send to Elasticsearch. + -*default*: `[]` +*default*: `[ 'authorization' ]` `elasticsearch.shardTimeout` added[4.2]:: How long Elasticsearch should wait for responses from shards. Set to 0 to disable. + diff --git a/src/plugins/elasticsearch/index.js b/src/plugins/elasticsearch/index.js index ad6405c37bcb0..590151142bc86 100644 --- a/src/plugins/elasticsearch/index.js +++ b/src/plugins/elasticsearch/index.js @@ -5,6 +5,8 @@ import healthCheck from './lib/health_check'; import exposeClient from './lib/expose_client'; import createProxy, { createPath } from './lib/create_proxy'; +const DEFAULT_REQUEST_HEADERS = [ 'authorization' ]; + module.exports = function ({ Plugin }) { return new Plugin({ require: ['kibana'], @@ -20,7 +22,7 @@ module.exports = function ({ Plugin }) { password: string(), shardTimeout: number().default(0), requestTimeout: number().default(30000), - requestHeaders: array().items(string()).single().default([]), + requestHeaders: array().items(string()).single().default(DEFAULT_REQUEST_HEADERS), pingTimeout: number().default(ref('requestTimeout')), startupTimeout: number().default(5000), ssl: object({ From 81631ffc2e748ddb151145fa131ad85316b6fc62 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Thu, 14 Apr 2016 17:50:24 -0700 Subject: [PATCH 10/18] Extracting filterHeader into its own module for reuse --- .../elasticsearch/lib/filter_headers.js | 18 ++++++++++++++++++ src/plugins/elasticsearch/lib/map_uri.js | 19 +++---------------- 2 files changed, 21 insertions(+), 16 deletions(-) create mode 100644 src/plugins/elasticsearch/lib/filter_headers.js diff --git a/src/plugins/elasticsearch/lib/filter_headers.js b/src/plugins/elasticsearch/lib/filter_headers.js new file mode 100644 index 0000000000000..988772f9623eb --- /dev/null +++ b/src/plugins/elasticsearch/lib/filter_headers.js @@ -0,0 +1,18 @@ +import _ from 'lodash'; + +module.exports = function (originalHeaders, headersToKeep) { + + const normalizeHeader = function (header) { + return header.trim().toLowerCase(); + }; + + // Normalize list of headers we want to allow in upstream request + const headersToKeepNormalized = headersToKeep.map(normalizeHeader); + + // Normalize original headers in request + const originalHeadersNormalized = _.mapKeys(originalHeaders, function (headerValue, headerName) { + return normalizeHeader(headerName); + }); + + return _.pick(originalHeaders, headersToKeepNormalized); +}; diff --git a/src/plugins/elasticsearch/lib/map_uri.js b/src/plugins/elasticsearch/lib/map_uri.js index 5f68ec6cdf05a..cc6c4d77ac309 100644 --- a/src/plugins/elasticsearch/lib/map_uri.js +++ b/src/plugins/elasticsearch/lib/map_uri.js @@ -1,23 +1,9 @@ import querystring from 'querystring'; import { resolve } from 'url'; -import _ from 'lodash'; +import filterHeaders from './filter_headers'; module.exports = function mapUri(server, prefix) { - const normalizeHeader = function (header) { - return header.trim().toLowerCase(); - }; - - const filterHeaders = function (originalHeaders) { - const originalHeadersNormalized = _.mapKeys(originalHeaders, function (headerValue, headerName) { - return normalizeHeader(headerName); - }); - const headersToKeep = server.config().get('elasticsearch.requestHeaders'); - const headersToKeepNormalized = headersToKeep.map(normalizeHeader); - - return _.pick(originalHeaders, headersToKeepNormalized); - }; - const config = server.config(); return function (request, done) { const path = request.path.replace('/elasticsearch', ''); @@ -28,6 +14,7 @@ module.exports = function mapUri(server, prefix) { } const query = querystring.stringify(request.query); if (query) url += '?' + query; - done(null, url, filterHeaders(request.headers)); + const filteredHeaders = filterHeaders(request.headers, server.config().get('elasticsearch.requestHeaders')); + done(null, url, filteredHeaders); }; }; From f9f4b79626095f8f12fd1ad69ebd833893c46a61 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Thu, 14 Apr 2016 17:55:28 -0700 Subject: [PATCH 11/18] Allow for elasticsearch.requestHeaders to be set to null --- config/kibana.yml | 4 +++- docs/kibana-yml.asciidoc | 1 + docs/settings.asciidoc | 2 +- src/plugins/elasticsearch/index.js | 2 +- 4 files changed, 6 insertions(+), 3 deletions(-) diff --git a/config/kibana.yml b/config/kibana.yml index 44c822328dab1..9fcfbc46f01a1 100644 --- a/config/kibana.yml +++ b/config/kibana.yml @@ -61,7 +61,9 @@ # must be a positive integer. # elasticsearch.requestTimeout: 30000 -# List of Kibana client-side headers to send to Elasticsearch +# List of Kibana client-side headers to send to Elasticsearch. To send *no* client-side +# headers, set this value to null, not []. Setting it to [] will send the default list +# of headers. # elasticsearch.requestHeaders: [ authorization ] # Time in milliseconds for Elasticsearch to wait for responses from shards. Set to 0 to disable. diff --git a/docs/kibana-yml.asciidoc b/docs/kibana-yml.asciidoc index b42dcc88eeb44..ed9bc73e46c3e 100644 --- a/docs/kibana-yml.asciidoc +++ b/docs/kibana-yml.asciidoc @@ -30,6 +30,7 @@ wait for Elasticsearch to respond to pings. `elasticsearch.requestTimeout:`:: *Default: 300000* Time in milliseconds to wait for responses from the back end or Elasticsearch. This value must be a positive integer. `elasticsearch.requestHeaders:`:: *Default: `[ 'authorization' ]`* List of Kibana client-side headers to send to Elasticsearch. +To send *no* client-side headers, set this value to null, not []. Setting it to [] will send the default list of headers. `elasticsearch.shardTimeout:`:: *Default: 0* Time in milliseconds for Elasticsearch to wait for responses from shards. Set to 0 to disable. `elasticsearch.startupTimeout:`:: *Default: 5000* Time in milliseconds to wait for Elasticsearch at Kibana startup before diff --git a/docs/settings.asciidoc b/docs/settings.asciidoc index d871abcc92c38..d8957022e0e59 100644 --- a/docs/settings.asciidoc +++ b/docs/settings.asciidoc @@ -376,7 +376,7 @@ deprecated[4.2, The names of several Kibana server properties changed in the 4.2 + *default*: `500000` -`elasticsearch.requestHeaders:` added[5.0]:: List of Kibana client-side headers to send to Elasticsearch. +`elasticsearch.requestHeaders:` added[5.0]:: List of Kibana client-side headers to send to Elasticsearch. To send *no* client-side headers, set this value to null, not []. Setting it to [] will send the default list of headers. + *default*: `[ 'authorization' ]` diff --git a/src/plugins/elasticsearch/index.js b/src/plugins/elasticsearch/index.js index 590151142bc86..97fd00f74aa8e 100644 --- a/src/plugins/elasticsearch/index.js +++ b/src/plugins/elasticsearch/index.js @@ -22,7 +22,7 @@ module.exports = function ({ Plugin }) { password: string(), shardTimeout: number().default(0), requestTimeout: number().default(30000), - requestHeaders: array().items(string()).single().default(DEFAULT_REQUEST_HEADERS), + requestHeaders: array().items().single().default(DEFAULT_REQUEST_HEADERS), pingTimeout: number().default(ref('requestTimeout')), startupTimeout: number().default(5000), ssl: object({ From 7f75efb43a688c7e61d04d723c24b14da31a7a32 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Thu, 14 Apr 2016 18:12:16 -0700 Subject: [PATCH 12/18] Make callWithRequest use whitelisted headers --- src/plugins/elasticsearch/lib/call_with_request.js | 8 ++++---- src/plugins/elasticsearch/lib/expose_client.js | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/plugins/elasticsearch/lib/call_with_request.js b/src/plugins/elasticsearch/lib/call_with_request.js index 385f3e61cc986..35b4ba32be1de 100644 --- a/src/plugins/elasticsearch/lib/call_with_request.js +++ b/src/plugins/elasticsearch/lib/call_with_request.js @@ -3,12 +3,12 @@ import Promise from 'bluebird'; import Boom from 'boom'; import getBasicAuthRealm from './get_basic_auth_realm'; import toPath from 'lodash/internal/toPath'; +import filterHeaders from './filter_headers'; -module.exports = (client) => { +module.exports = (server, client) => { return (req, endpoint, params = {}) => { - if (req.headers.authorization) { - _.set(params, 'headers.authorization', req.headers.authorization); - } + const filteredHeaders = filterHeaders(req.headers, server.config().get('elasticsearch.requestHeaders')); + _.set(params, 'headers', filteredHeaders); const path = toPath(endpoint); const api = _.get(client, path); let apiContext = _.get(client, path.slice(0, -1)); diff --git a/src/plugins/elasticsearch/lib/expose_client.js b/src/plugins/elasticsearch/lib/expose_client.js index 0c81c07d25c7f..299b85bdd5c3d 100644 --- a/src/plugins/elasticsearch/lib/expose_client.js +++ b/src/plugins/elasticsearch/lib/expose_client.js @@ -78,8 +78,8 @@ module.exports = function (server) { server.expose('ElasticsearchClientLogging', ElasticsearchClientLogging); server.expose('client', client); server.expose('createClient', createClient); - server.expose('callWithRequestFactory', callWithRequest); - server.expose('callWithRequest', callWithRequest(noAuthClient)); + server.expose('callWithRequestFactory', _.partial(callWithRequest, server)); + server.expose('callWithRequest', callWithRequest(server, noAuthClient)); server.expose('errors', elasticsearch.errors); return client; From 2149f84abaceeb18babf242d6e796b6c134333be Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 15 Apr 2016 06:11:07 -0700 Subject: [PATCH 13/18] Renaming property to make its intent more explicit --- config/kibana.yml | 2 +- docs/kibana-yml.asciidoc | 2 +- docs/settings.asciidoc | 2 +- src/plugins/elasticsearch/index.js | 2 +- src/plugins/elasticsearch/lib/__tests__/map_uri.js | 2 +- src/plugins/elasticsearch/lib/call_with_request.js | 2 +- src/plugins/elasticsearch/lib/map_uri.js | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/config/kibana.yml b/config/kibana.yml index 9fcfbc46f01a1..eb9ee3e5e7f2c 100644 --- a/config/kibana.yml +++ b/config/kibana.yml @@ -64,7 +64,7 @@ # List of Kibana client-side headers to send to Elasticsearch. To send *no* client-side # headers, set this value to null, not []. Setting it to [] will send the default list # of headers. -# elasticsearch.requestHeaders: [ authorization ] +# elasticsearch.requestHeadersWhitelist: [ authorization ] # Time in milliseconds for Elasticsearch to wait for responses from shards. Set to 0 to disable. # elasticsearch.shardTimeout: 0 diff --git a/docs/kibana-yml.asciidoc b/docs/kibana-yml.asciidoc index ed9bc73e46c3e..41b6d081380af 100644 --- a/docs/kibana-yml.asciidoc +++ b/docs/kibana-yml.asciidoc @@ -29,7 +29,7 @@ to `false`. wait for Elasticsearch to respond to pings. `elasticsearch.requestTimeout:`:: *Default: 300000* Time in milliseconds to wait for responses from the back end or Elasticsearch. This value must be a positive integer. -`elasticsearch.requestHeaders:`:: *Default: `[ 'authorization' ]`* List of Kibana client-side headers to send to Elasticsearch. +`elasticsearch.requestHeadersWhitelist:`:: *Default: `[ 'authorization' ]`* List of Kibana client-side headers to send to Elasticsearch. To send *no* client-side headers, set this value to null, not []. Setting it to [] will send the default list of headers. `elasticsearch.shardTimeout:`:: *Default: 0* Time in milliseconds for Elasticsearch to wait for responses from shards. Set to 0 to disable. diff --git a/docs/settings.asciidoc b/docs/settings.asciidoc index d8957022e0e59..eda557ca00055 100644 --- a/docs/settings.asciidoc +++ b/docs/settings.asciidoc @@ -376,7 +376,7 @@ deprecated[4.2, The names of several Kibana server properties changed in the 4.2 + *default*: `500000` -`elasticsearch.requestHeaders:` added[5.0]:: List of Kibana client-side headers to send to Elasticsearch. To send *no* client-side headers, set this value to null, not []. Setting it to [] will send the default list of headers. +`elasticsearch.requestHeadersWhitelist:` added[5.0]:: List of Kibana client-side headers to send to Elasticsearch. To send *no* client-side headers, set this value to null, not []. Setting it to [] will send the default list of headers. + *default*: `[ 'authorization' ]` diff --git a/src/plugins/elasticsearch/index.js b/src/plugins/elasticsearch/index.js index 97fd00f74aa8e..7496e52403cb1 100644 --- a/src/plugins/elasticsearch/index.js +++ b/src/plugins/elasticsearch/index.js @@ -22,7 +22,7 @@ module.exports = function ({ Plugin }) { password: string(), shardTimeout: number().default(0), requestTimeout: number().default(30000), - requestHeaders: array().items().single().default(DEFAULT_REQUEST_HEADERS), + requestHeadersWhitelist: array().items().single().default(DEFAULT_REQUEST_HEADERS), pingTimeout: number().default(ref('requestTimeout')), startupTimeout: number().default(5000), ssl: object({ diff --git a/src/plugins/elasticsearch/lib/__tests__/map_uri.js b/src/plugins/elasticsearch/lib/__tests__/map_uri.js index ef9b0cf73356f..5466be547c753 100644 --- a/src/plugins/elasticsearch/lib/__tests__/map_uri.js +++ b/src/plugins/elasticsearch/lib/__tests__/map_uri.js @@ -11,7 +11,7 @@ describe('plugins/elasticsearch', function () { beforeEach(function () { const get = sinon.stub() .withArgs('elasticsearch.url').returns('http://foobar:9200') - .withArgs('elasticsearch.requestHeaders').returns(['x-my-custom-HEADER', 'Authorization']); + .withArgs('elasticsearch.requestHeadersWhitelist').returns(['x-my-custom-HEADER', 'Authorization']); const config = function () { return { get: get }; }; server = { config: config diff --git a/src/plugins/elasticsearch/lib/call_with_request.js b/src/plugins/elasticsearch/lib/call_with_request.js index 35b4ba32be1de..870b856de5fce 100644 --- a/src/plugins/elasticsearch/lib/call_with_request.js +++ b/src/plugins/elasticsearch/lib/call_with_request.js @@ -7,7 +7,7 @@ import filterHeaders from './filter_headers'; module.exports = (server, client) => { return (req, endpoint, params = {}) => { - const filteredHeaders = filterHeaders(req.headers, server.config().get('elasticsearch.requestHeaders')); + const filteredHeaders = filterHeaders(req.headers, server.config().get('elasticsearch.requestHeadersWhitelist')); _.set(params, 'headers', filteredHeaders); const path = toPath(endpoint); const api = _.get(client, path); diff --git a/src/plugins/elasticsearch/lib/map_uri.js b/src/plugins/elasticsearch/lib/map_uri.js index cc6c4d77ac309..38068ffa35809 100644 --- a/src/plugins/elasticsearch/lib/map_uri.js +++ b/src/plugins/elasticsearch/lib/map_uri.js @@ -14,7 +14,7 @@ module.exports = function mapUri(server, prefix) { } const query = querystring.stringify(request.query); if (query) url += '?' + query; - const filteredHeaders = filterHeaders(request.headers, server.config().get('elasticsearch.requestHeaders')); + const filteredHeaders = filterHeaders(request.headers, server.config().get('elasticsearch.requestHeadersWhitelist')); done(null, url, filteredHeaders); }; }; From c5ce30f81cb4fe760a2976df4bc2f7c7a5c9f0fa Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 15 Apr 2016 12:15:58 -0700 Subject: [PATCH 14/18] Adding tests for empty and [] values for elasticsearch.requestHeadersWhitelist --- .../elasticsearch/lib/__tests__/map_uri.js | 53 +++++++++++++++---- .../elasticsearch/lib/filter_headers.js | 4 ++ 2 files changed, 47 insertions(+), 10 deletions(-) diff --git a/src/plugins/elasticsearch/lib/__tests__/map_uri.js b/src/plugins/elasticsearch/lib/__tests__/map_uri.js index 5466be547c753..540ff912e7ba1 100644 --- a/src/plugins/elasticsearch/lib/__tests__/map_uri.js +++ b/src/plugins/elasticsearch/lib/__tests__/map_uri.js @@ -5,18 +5,9 @@ import sinon from 'sinon'; describe('plugins/elasticsearch', function () { describe('lib/map_uri', function () { - let server; let request; beforeEach(function () { - const get = sinon.stub() - .withArgs('elasticsearch.url').returns('http://foobar:9200') - .withArgs('elasticsearch.requestHeadersWhitelist').returns(['x-my-custom-HEADER', 'Authorization']); - const config = function () { return { get: get }; }; - server = { - config: config - }; - request = { path: '/elasticsearch/some/path', headers: { @@ -31,7 +22,16 @@ describe('plugins/elasticsearch', function () { }; }); - it('only keeps the whitelisted request headers', function () { + it('only sends the whitelisted request headers', function () { + + const get = sinon.stub() + .withArgs('elasticsearch.url').returns('http://foobar:9200') + .withArgs('elasticsearch.requestHeadersWhitelist').returns(['x-my-custom-HEADER', 'Authorization']); + const config = function () { return { get: get }; }; + const server = { + config: config + }; + mapUri(server)(request, function (err, upstreamUri, upstreamHeaders) { expect(err).to.be(null); expect(upstreamHeaders).to.have.property('authorization'); @@ -39,5 +39,38 @@ describe('plugins/elasticsearch', function () { expect(Object.keys(upstreamHeaders).length).to.be(2); }); }); + + it('sends no headers if whitelist is set to []', function () { + + const get = sinon.stub() + .withArgs('elasticsearch.url').returns('http://foobar:9200') + .withArgs('elasticsearch.requestHeadersWhitelist').returns([]); + const config = function () { return { get: get }; }; + const server = { + config: config + }; + + mapUri(server)(request, function (err, upstreamUri, upstreamHeaders) { + expect(err).to.be(null); + expect(Object.keys(upstreamHeaders).length).to.be(0); + }); + }); + + it('sends no headers if whitelist is set to no value', function () { + + const get = sinon.stub() + .withArgs('elasticsearch.url').returns('http://foobar:9200') + .withArgs('elasticsearch.requestHeadersWhitelist').returns([ null ]); // This is how Joi returns it + const config = function () { return { get: get }; }; + const server = { + config: config + }; + + mapUri(server)(request, function (err, upstreamUri, upstreamHeaders) { + expect(err).to.be(null); + expect(Object.keys(upstreamHeaders).length).to.be(0); + }); + }); + }); }); diff --git a/src/plugins/elasticsearch/lib/filter_headers.js b/src/plugins/elasticsearch/lib/filter_headers.js index 988772f9623eb..91a37278f08c1 100644 --- a/src/plugins/elasticsearch/lib/filter_headers.js +++ b/src/plugins/elasticsearch/lib/filter_headers.js @@ -3,6 +3,10 @@ import _ from 'lodash'; module.exports = function (originalHeaders, headersToKeep) { const normalizeHeader = function (header) { + if (!header) { + return ''; + } + header = header.toString(); return header.trim().toLowerCase(); }; From 304511715ea5eb24d949cb06f62539455792510a Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 15 Apr 2016 12:23:43 -0700 Subject: [PATCH 15/18] Updating documentation --- config/kibana.yml | 4 ++-- docs/kibana-yml.asciidoc | 3 ++- docs/settings.asciidoc | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/config/kibana.yml b/config/kibana.yml index eb9ee3e5e7f2c..211cf88b98d65 100644 --- a/config/kibana.yml +++ b/config/kibana.yml @@ -62,8 +62,8 @@ # elasticsearch.requestTimeout: 30000 # List of Kibana client-side headers to send to Elasticsearch. To send *no* client-side -# headers, set this value to null, not []. Setting it to [] will send the default list -# of headers. +# headers, set this value to nothing/blank (i.e. elasticsearch.requestHeadersWhitelist: ) +# or the empty list (i.e. elasticsearch.requestHeadersWhitelist: []) # elasticsearch.requestHeadersWhitelist: [ authorization ] # Time in milliseconds for Elasticsearch to wait for responses from shards. Set to 0 to disable. diff --git a/docs/kibana-yml.asciidoc b/docs/kibana-yml.asciidoc index 41b6d081380af..d0ea0ed486392 100644 --- a/docs/kibana-yml.asciidoc +++ b/docs/kibana-yml.asciidoc @@ -30,7 +30,8 @@ wait for Elasticsearch to respond to pings. `elasticsearch.requestTimeout:`:: *Default: 300000* Time in milliseconds to wait for responses from the back end or Elasticsearch. This value must be a positive integer. `elasticsearch.requestHeadersWhitelist:`:: *Default: `[ 'authorization' ]`* List of Kibana client-side headers to send to Elasticsearch. -To send *no* client-side headers, set this value to null, not []. Setting it to [] will send the default list of headers. +To send *no* client-side headers, set this value to nothing/blank (i.e. elasticsearch.requestHeadersWhitelist: ) or the empty list +(i.e. elasticsearch.requestHeadersWhitelist: []). `elasticsearch.shardTimeout:`:: *Default: 0* Time in milliseconds for Elasticsearch to wait for responses from shards. Set to 0 to disable. `elasticsearch.startupTimeout:`:: *Default: 5000* Time in milliseconds to wait for Elasticsearch at Kibana startup before diff --git a/docs/settings.asciidoc b/docs/settings.asciidoc index eda557ca00055..9c34eab8c3d6d 100644 --- a/docs/settings.asciidoc +++ b/docs/settings.asciidoc @@ -376,7 +376,7 @@ deprecated[4.2, The names of several Kibana server properties changed in the 4.2 + *default*: `500000` -`elasticsearch.requestHeadersWhitelist:` added[5.0]:: List of Kibana client-side headers to send to Elasticsearch. To send *no* client-side headers, set this value to null, not []. Setting it to [] will send the default list of headers. +`elasticsearch.requestHeadersWhitelist:` added[5.0]:: List of Kibana client-side headers to send to Elasticsearch. To send *no* client-side headers, set this value to nothing/blank (i.e. elasticsearch.requestHeadersWhitelist: ) or the empty list (i.e. elasticsearch.requestHeadersWhitelist: []). + *default*: `[ 'authorization' ]` From 89dfbeee1c27313033b679cdfd1e9477a3a4d591 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Sat, 16 Apr 2016 05:49:56 -0700 Subject: [PATCH 16/18] Use es6 export default syntax --- src/plugins/elasticsearch/lib/filter_headers.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/elasticsearch/lib/filter_headers.js b/src/plugins/elasticsearch/lib/filter_headers.js index 91a37278f08c1..285b45f560697 100644 --- a/src/plugins/elasticsearch/lib/filter_headers.js +++ b/src/plugins/elasticsearch/lib/filter_headers.js @@ -1,6 +1,6 @@ import _ from 'lodash'; -module.exports = function (originalHeaders, headersToKeep) { +export default function (originalHeaders, headersToKeep) { const normalizeHeader = function (header) { if (!header) { @@ -19,4 +19,4 @@ module.exports = function (originalHeaders, headersToKeep) { }); return _.pick(originalHeaders, headersToKeepNormalized); -}; +} From 65ddf96d34cd631b5c63168f30faeb974449dbc7 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Sat, 16 Apr 2016 05:52:54 -0700 Subject: [PATCH 17/18] Only document one option for sending no headers --- config/kibana.yml | 3 +-- docs/kibana-yml.asciidoc | 3 +-- docs/settings.asciidoc | 2 +- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/config/kibana.yml b/config/kibana.yml index 211cf88b98d65..8694f65c61a5a 100644 --- a/config/kibana.yml +++ b/config/kibana.yml @@ -62,8 +62,7 @@ # elasticsearch.requestTimeout: 30000 # List of Kibana client-side headers to send to Elasticsearch. To send *no* client-side -# headers, set this value to nothing/blank (i.e. elasticsearch.requestHeadersWhitelist: ) -# or the empty list (i.e. elasticsearch.requestHeadersWhitelist: []) +# headers, set this value to [] (an empty list). # elasticsearch.requestHeadersWhitelist: [ authorization ] # Time in milliseconds for Elasticsearch to wait for responses from shards. Set to 0 to disable. diff --git a/docs/kibana-yml.asciidoc b/docs/kibana-yml.asciidoc index d0ea0ed486392..2ee6aa698756e 100644 --- a/docs/kibana-yml.asciidoc +++ b/docs/kibana-yml.asciidoc @@ -30,8 +30,7 @@ wait for Elasticsearch to respond to pings. `elasticsearch.requestTimeout:`:: *Default: 300000* Time in milliseconds to wait for responses from the back end or Elasticsearch. This value must be a positive integer. `elasticsearch.requestHeadersWhitelist:`:: *Default: `[ 'authorization' ]`* List of Kibana client-side headers to send to Elasticsearch. -To send *no* client-side headers, set this value to nothing/blank (i.e. elasticsearch.requestHeadersWhitelist: ) or the empty list -(i.e. elasticsearch.requestHeadersWhitelist: []). +To send *no* client-side headers, set this value to [] (an empty list). `elasticsearch.shardTimeout:`:: *Default: 0* Time in milliseconds for Elasticsearch to wait for responses from shards. Set to 0 to disable. `elasticsearch.startupTimeout:`:: *Default: 5000* Time in milliseconds to wait for Elasticsearch at Kibana startup before diff --git a/docs/settings.asciidoc b/docs/settings.asciidoc index 9c34eab8c3d6d..1b99bec2a9307 100644 --- a/docs/settings.asciidoc +++ b/docs/settings.asciidoc @@ -376,7 +376,7 @@ deprecated[4.2, The names of several Kibana server properties changed in the 4.2 + *default*: `500000` -`elasticsearch.requestHeadersWhitelist:` added[5.0]:: List of Kibana client-side headers to send to Elasticsearch. To send *no* client-side headers, set this value to nothing/blank (i.e. elasticsearch.requestHeadersWhitelist: ) or the empty list (i.e. elasticsearch.requestHeadersWhitelist: []). +`elasticsearch.requestHeadersWhitelist:` added[5.0]:: List of Kibana client-side headers to send to Elasticsearch. To send *no* client-side headers, set this value to [] (an empty list). + *default*: `[ 'authorization' ]` From 028db3fd0a956deb3b5b4766545f7646783d3bb4 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Sat, 16 Apr 2016 08:11:39 -0700 Subject: [PATCH 18/18] Adding back x-forwarded-* headers --- src/plugins/elasticsearch/lib/create_proxy.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/plugins/elasticsearch/lib/create_proxy.js b/src/plugins/elasticsearch/lib/create_proxy.js index 8b1acb9ddb0c9..2ef2bd1da56d8 100644 --- a/src/plugins/elasticsearch/lib/create_proxy.js +++ b/src/plugins/elasticsearch/lib/create_proxy.js @@ -17,6 +17,7 @@ function createProxy(server, method, route, config) { proxy: { mapUri: mapUri(server), agent: createAgent(server), + xforward: true, timeout: server.config().get('elasticsearch.requestTimeout'), onResponse: function (err, responseFromUpstream, request, reply) { reply(err, responseFromUpstream);