mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
Merge remote-tracking branch 'sense/master' into feature/console
This commit is contained in:
commit
1728cf84a1
12 changed files with 734 additions and 38 deletions
21
.travis.yml
Normal file
21
.travis.yml
Normal file
|
@ -0,0 +1,21 @@
|
|||
language: node_js
|
||||
node_js: 4
|
||||
env:
|
||||
- CXX=g++-4.8
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
packages:
|
||||
- g++-4.8
|
||||
|
||||
install:
|
||||
- npm install
|
||||
- npm run setup_kibana
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- node_modules
|
||||
- ../kibana
|
||||
|
||||
script: npm test
|
|
@ -86,6 +86,33 @@ You can add the following options in the `config/kibana.yml` file:
|
|||
`sense.proxyFilter`:: A list of regular expressions that are used to validate any outgoing request from Sense. If none
|
||||
of these match, the request will be rejected. Defaults to `.*` . See <<securing_sense>> for more details.
|
||||
|
||||
`sense.proxyConfig`:: A list of configuration options that are based on the proxy target. Use this to set custom timeouts or SSL settings for specific hosts. This is done by defining a set of `match` criteria using wildcards/globs which will be checked against each request. The configuration from all matching rules will then be merged together to configure the proxy used for that request.
|
||||
+
|
||||
The valid match keys are `match.protocol`, `match.host`, `match.port`, and `match.path`. All of these keys default to `*`, which means they will match any value.
|
||||
+
|
||||
Example:
|
||||
+
|
||||
[source,yaml]
|
||||
--------
|
||||
sense.proxyConfig:
|
||||
- match:
|
||||
host: "*.internal.org" # allow any host that ends in .internal.org
|
||||
port: "{9200..9299}" # allow any port from 9200-9299
|
||||
|
||||
ssl:
|
||||
ca: "/opt/certs/internal.ca"
|
||||
# "key" and "cert" are also valid options here
|
||||
|
||||
- match:
|
||||
protocol: "https"
|
||||
|
||||
ssl:
|
||||
verify: false # allows any certificate to be used, even self-signed certs
|
||||
|
||||
# since this rule has no "match" section it matches everything
|
||||
- timeout: 180000 # 3 minutes
|
||||
--------
|
||||
|
||||
[NOTE]
|
||||
|
||||
Kibana needs to be restarted after each change to the configuration for them to be applied.
|
||||
|
@ -101,7 +128,5 @@ Once downloaded you can install Sense using the following command:
|
|||
|
||||
[source,bash]
|
||||
-------------
|
||||
$ bin/kibana plugin -i sense -u file://PATH_TO_SENSE_TAR_FILE
|
||||
$ bin/kibana plugin -i sense -u file:///PATH_TO_SENSE_TAR_FILE
|
||||
-------------
|
||||
|
||||
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import { ProxyConfigCollection } from './server/proxy_config_collection';
|
||||
|
||||
module.exports = function (kibana) {
|
||||
let { resolve, join, sep } = require('path');
|
||||
let Joi = require('joi');
|
||||
|
@ -37,53 +39,117 @@ module.exports = function (kibana) {
|
|||
defaultServerUrl: Joi.string().default('http://localhost:9200'),
|
||||
proxyFilter: Joi.array().items(Joi.string()).single().default(['.*']),
|
||||
ssl: Joi.object({
|
||||
verify: Joi.boolean().default(true),
|
||||
verify: Joi.boolean(),
|
||||
}).default(),
|
||||
proxyConfig: Joi.array().items(
|
||||
Joi.object().keys({
|
||||
match: Joi.object().keys({
|
||||
protocol: Joi.string().default('*'),
|
||||
host: Joi.string().default('*'),
|
||||
port: Joi.string().default('*'),
|
||||
path: Joi.string().default('*')
|
||||
}),
|
||||
|
||||
timeout: Joi.number(),
|
||||
ssl: Joi.object().keys({
|
||||
verify: Joi.boolean(),
|
||||
ca: Joi.array().single().items(Joi.string()),
|
||||
cert: Joi.string(),
|
||||
key: Joi.string()
|
||||
}).default()
|
||||
})
|
||||
).default([
|
||||
{
|
||||
match: {
|
||||
protocol: '*',
|
||||
host: '*',
|
||||
port: '*',
|
||||
path: '*'
|
||||
},
|
||||
|
||||
timeout: 180000,
|
||||
ssl: {
|
||||
verify: true
|
||||
}
|
||||
}
|
||||
])
|
||||
}).default();
|
||||
},
|
||||
|
||||
init: function (server, options) {
|
||||
const filters = options.proxyFilter.map(str => new RegExp(str));
|
||||
|
||||
// http://hapijs.com/api/8.8.1#route-configuration
|
||||
server.route({
|
||||
path: '/api/console/proxy',
|
||||
method: ['*', 'GET'],
|
||||
config: {
|
||||
handler: {
|
||||
proxy: {
|
||||
mapUri: function (req, cb) {
|
||||
let { uri } = req.query;
|
||||
if (!uri) {
|
||||
cb(Boom.badRequest('URI is a required param.'));
|
||||
return;
|
||||
}
|
||||
if (options.ssl && options.ssl.verify) {
|
||||
throw new Error('sense.ssl.verify is no longer supported.');
|
||||
}
|
||||
|
||||
if (!filters.some(re => re.test(uri))) {
|
||||
const err = Boom.forbidden();
|
||||
err.output.payload = "Error connecting to '" + uri + "':\n\nUnable to send requests to that url.";
|
||||
err.output.headers['content-type'] = 'text/plain';
|
||||
cb(err);
|
||||
return;
|
||||
}
|
||||
const proxyConfigCollection = new ProxyConfigCollection(options.proxyConfig);
|
||||
const proxyRouteConfig = {
|
||||
validate: {
|
||||
query: Joi.object().keys({
|
||||
uri: Joi.string().uri({
|
||||
allowRelative: false,
|
||||
shema: ['http:', 'https:'],
|
||||
}),
|
||||
}).unknown(true),
|
||||
},
|
||||
|
||||
cb(null, uri);
|
||||
},
|
||||
rejectUnauthorized: options.ssl.verify,
|
||||
passThrough: true,
|
||||
xforward: true,
|
||||
onResponse: function (err, res, request, reply, settings, ttl) {
|
||||
if (err != null) {
|
||||
reply("Error connecting to '" + request.query.uri + "':\n\n" + err.message).type("text/plain").statusCode = 502;
|
||||
} else {
|
||||
reply(null, res);
|
||||
}
|
||||
}
|
||||
pre: [
|
||||
function filterUri(req, reply) {
|
||||
const { uri } = req.query;
|
||||
|
||||
if (!filters.some(re => re.test(uri))) {
|
||||
const err = Boom.forbidden();
|
||||
err.output.payload = "Error connecting to '" + uri + "':\n\nUnable to send requests to that url.";
|
||||
err.output.headers['content-type'] = 'text/plain';
|
||||
reply(err);
|
||||
} else {
|
||||
reply();
|
||||
}
|
||||
}
|
||||
],
|
||||
|
||||
handler(req, reply) {
|
||||
const { uri } = req.query;
|
||||
|
||||
reply.proxy({
|
||||
uri,
|
||||
xforward: true,
|
||||
passThrough: true,
|
||||
onResponse(err, res, request, reply, settings, ttl) {
|
||||
if (err != null) {
|
||||
reply("Error connecting to '" + request.query.uri + "':\n\n" + err.message).type("text/plain").statusCode = 502;
|
||||
} else {
|
||||
reply(null, res);
|
||||
}
|
||||
},
|
||||
|
||||
...proxyConfigCollection.configForUri(uri)
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
server.route({
|
||||
path: '/api/console/proxy',
|
||||
method: '*',
|
||||
config: {
|
||||
...proxyRouteConfig,
|
||||
|
||||
payload: {
|
||||
output: 'stream',
|
||||
parse: false
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
path: '/api/console/proxy',
|
||||
method: 'GET',
|
||||
config: {
|
||||
...proxyRouteConfig
|
||||
}
|
||||
})
|
||||
|
||||
server.route({
|
||||
path: '/api/console/api_server',
|
||||
method: ['GET', 'POST'],
|
||||
|
|
|
@ -10,7 +10,7 @@ require('ui/modules')
|
|||
// require the root app code, which expects to execute once the dom is loaded up
|
||||
require('../app');
|
||||
|
||||
const ConfigTemplate = require('ui/ConfigTemplate');
|
||||
const ConfigTemplate = require('ui/config_template');
|
||||
const input = require('../input');
|
||||
const es = require('../es');
|
||||
const storage = require('../storage');
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
<h4>Quick intro to the UI</h4>
|
||||
|
||||
<p>Sense is split into two panes: an editor pane (white) and a response pane (black).
|
||||
<p>Sense is split into two panes: an editor pane (left) and a response pane (right).
|
||||
Use the editor to type requests and submit them to Elasticsearch. The results will be displayed in
|
||||
the response pane on the right side.
|
||||
</p>
|
||||
|
|
234
src/plugins/console/server/__tests__/proxy_config.js
Normal file
234
src/plugins/console/server/__tests__/proxy_config.js
Normal file
|
@ -0,0 +1,234 @@
|
|||
/* eslint-env mocha */
|
||||
|
||||
import expect from 'expect.js';
|
||||
import sinon from 'sinon';
|
||||
import fs from 'fs';
|
||||
import https, { Agent as HttpsAgent } from 'https';
|
||||
import { parse as parseUrl } from 'url';
|
||||
|
||||
import { ProxyConfig } from '../proxy_config'
|
||||
|
||||
const matchGoogle = {
|
||||
protocol: 'https',
|
||||
host: 'google.com',
|
||||
path: '/search'
|
||||
}
|
||||
const parsedGoogle = parseUrl('https://google.com/search');
|
||||
const parsedLocalEs = parseUrl('https://localhost:5601/search');
|
||||
|
||||
describe('ProxyConfig', function () {
|
||||
beforeEach(function () {
|
||||
sinon.stub(fs, 'readFileSync', function (path) {
|
||||
return { path }
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
fs.readFileSync.restore();
|
||||
});
|
||||
|
||||
describe('constructor', function () {
|
||||
beforeEach(function () {
|
||||
sinon.stub(https, 'Agent');
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
https.Agent.restore();
|
||||
});
|
||||
|
||||
it('uses ca to create sslAgent', function () {
|
||||
const config = new ProxyConfig({
|
||||
ssl: {
|
||||
ca: ['path/to/ca']
|
||||
}
|
||||
});
|
||||
|
||||
expect(config.sslAgent).to.be.a(https.Agent);
|
||||
sinon.assert.calledOnce(https.Agent);
|
||||
const sslAgentOpts = https.Agent.firstCall.args[0];
|
||||
expect(sslAgentOpts).to.eql({
|
||||
ca: [{ path: 'path/to/ca' }],
|
||||
cert: undefined,
|
||||
key: undefined,
|
||||
});
|
||||
});
|
||||
|
||||
it('uses cert, and key to create sslAgent', function () {
|
||||
const config = new ProxyConfig({
|
||||
ssl: {
|
||||
cert: 'path/to/cert',
|
||||
key: 'path/to/key'
|
||||
}
|
||||
});
|
||||
|
||||
expect(config.sslAgent).to.be.a(https.Agent);
|
||||
sinon.assert.calledOnce(https.Agent);
|
||||
const sslAgentOpts = https.Agent.firstCall.args[0];
|
||||
expect(sslAgentOpts).to.eql({
|
||||
ca: undefined,
|
||||
cert: { path: 'path/to/cert' },
|
||||
key: { path: 'path/to/key' },
|
||||
});
|
||||
});
|
||||
|
||||
it('uses ca, cert, and key to create sslAgent', function () {
|
||||
const config = new ProxyConfig({
|
||||
ssl: {
|
||||
ca: ['path/to/ca'],
|
||||
cert: 'path/to/cert',
|
||||
key: 'path/to/key'
|
||||
}
|
||||
});
|
||||
|
||||
expect(config.sslAgent).to.be.a(https.Agent);
|
||||
sinon.assert.calledOnce(https.Agent);
|
||||
const sslAgentOpts = https.Agent.firstCall.args[0];
|
||||
expect(sslAgentOpts).to.eql({
|
||||
ca: [{ path: 'path/to/ca' }],
|
||||
cert: { path: 'path/to/cert' },
|
||||
key: { path: 'path/to/key' },
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('#getForParsedUri', function () {
|
||||
context('parsed url does not match', function () {
|
||||
it('returns {}', function () {
|
||||
const config = new ProxyConfig({
|
||||
match: matchGoogle,
|
||||
timeout: 100
|
||||
});
|
||||
|
||||
expect(config.getForParsedUri(parsedLocalEs)).to.eql({});
|
||||
});
|
||||
});
|
||||
|
||||
context('parsed url does match', function () {
|
||||
it('assigns timeout value', function () {
|
||||
const football = {};
|
||||
const config = new ProxyConfig({
|
||||
match: matchGoogle,
|
||||
timeout: football
|
||||
});
|
||||
|
||||
expect(config.getForParsedUri(parsedGoogle).timeout).to.be(football);
|
||||
});
|
||||
|
||||
it('assigns ssl.verify to rejectUnauthorized', function () {
|
||||
const football = {};
|
||||
const config = new ProxyConfig({
|
||||
match: matchGoogle,
|
||||
ssl: {
|
||||
verify: football
|
||||
}
|
||||
});
|
||||
|
||||
expect(config.getForParsedUri(parsedGoogle).rejectUnauthorized).to.be(football);
|
||||
});
|
||||
|
||||
context('uri us http', function () {
|
||||
context('ca is set', function () {
|
||||
it('creates but does not output the agent', function () {
|
||||
const config = new ProxyConfig({
|
||||
ssl: {
|
||||
ca: ['path/to/ca']
|
||||
}
|
||||
});
|
||||
|
||||
expect(config.sslAgent).to.be.an(HttpsAgent);
|
||||
expect(config.getForParsedUri({ protocol: 'http:' }).agent).to.be(undefined);
|
||||
});
|
||||
});
|
||||
context('cert is set', function () {
|
||||
it('creates but does not output the agent', function () {
|
||||
const config = new ProxyConfig({
|
||||
ssl: {
|
||||
cert: 'path/to/cert'
|
||||
}
|
||||
});
|
||||
|
||||
expect(config.sslAgent).to.be.an(HttpsAgent);
|
||||
expect(config.getForParsedUri({ protocol: 'http:' }).agent).to.be(undefined);
|
||||
});
|
||||
});
|
||||
context('key is set', function () {
|
||||
it('creates but does not output the agent', function () {
|
||||
const config = new ProxyConfig({
|
||||
ssl: {
|
||||
key: 'path/to/key'
|
||||
}
|
||||
});
|
||||
|
||||
expect(config.sslAgent).to.be.an(HttpsAgent);
|
||||
expect(config.getForParsedUri({ protocol: 'http:' }).agent).to.be(undefined);
|
||||
});
|
||||
});
|
||||
context('cert + key are set', function () {
|
||||
it('creates but does not output the agent', function () {
|
||||
const config = new ProxyConfig({
|
||||
ssl: {
|
||||
cert: 'path/to/cert',
|
||||
key: 'path/to/key'
|
||||
}
|
||||
});
|
||||
|
||||
expect(config.sslAgent).to.be.an(HttpsAgent);
|
||||
expect(config.getForParsedUri({ protocol: 'http:' }).agent).to.be(undefined);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
context('uri us https', function () {
|
||||
context('ca is set', function () {
|
||||
it('creates and outputs the agent', function () {
|
||||
const config = new ProxyConfig({
|
||||
ssl: {
|
||||
ca: ['path/to/ca']
|
||||
}
|
||||
});
|
||||
|
||||
expect(config.sslAgent).to.be.an(HttpsAgent);
|
||||
expect(config.getForParsedUri({ protocol: 'https:' }).agent).to.be(config.sslAgent);
|
||||
});
|
||||
});
|
||||
context('cert is set', function () {
|
||||
it('creates and outputs the agent', function () {
|
||||
const config = new ProxyConfig({
|
||||
ssl: {
|
||||
cert: 'path/to/cert'
|
||||
}
|
||||
});
|
||||
|
||||
expect(config.sslAgent).to.be.an(HttpsAgent);
|
||||
expect(config.getForParsedUri({ protocol: 'https:' }).agent).to.be(config.sslAgent);
|
||||
});
|
||||
});
|
||||
context('key is set', function () {
|
||||
it('creates and outputs the agent', function () {
|
||||
const config = new ProxyConfig({
|
||||
ssl: {
|
||||
key: 'path/to/key'
|
||||
}
|
||||
});
|
||||
|
||||
expect(config.sslAgent).to.be.an(HttpsAgent);
|
||||
expect(config.getForParsedUri({ protocol: 'https:' }).agent).to.be(config.sslAgent);
|
||||
});
|
||||
});
|
||||
context('cert + key are set', function () {
|
||||
it('creates and outputs the agent', function () {
|
||||
const config = new ProxyConfig({
|
||||
ssl: {
|
||||
cert: 'path/to/cert',
|
||||
key: 'path/to/key'
|
||||
}
|
||||
});
|
||||
|
||||
expect(config.sslAgent).to.be.an(HttpsAgent);
|
||||
expect(config.getForParsedUri({ protocol: 'https:' }).agent).to.be(config.sslAgent);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
156
src/plugins/console/server/__tests__/proxy_config_collection.js
Normal file
156
src/plugins/console/server/__tests__/proxy_config_collection.js
Normal file
|
@ -0,0 +1,156 @@
|
|||
/* eslint-env mocha */
|
||||
|
||||
import expect from 'expect.js';
|
||||
import sinon from 'sinon';
|
||||
import fs from 'fs';
|
||||
import { Agent as HttpsAgent } from 'https';
|
||||
|
||||
import { ProxyConfigCollection } from '../proxy_config_collection'
|
||||
|
||||
describe('ProxyConfigCollection', function () {
|
||||
beforeEach(function () {
|
||||
sinon.stub(fs, 'readFileSync', () => new Buffer(0));
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
fs.readFileSync.restore();
|
||||
});
|
||||
|
||||
const proxyConfigs = [
|
||||
{
|
||||
match: {
|
||||
protocol: 'https',
|
||||
host: 'localhost',
|
||||
port: 5601,
|
||||
path: '/.kibana'
|
||||
},
|
||||
|
||||
timeout: 1,
|
||||
},
|
||||
|
||||
{
|
||||
match: {
|
||||
protocol: 'https',
|
||||
host: 'localhost',
|
||||
port: 5601
|
||||
},
|
||||
|
||||
timeout: 2,
|
||||
},
|
||||
|
||||
{
|
||||
match: {
|
||||
host: 'localhost',
|
||||
port: 5601
|
||||
},
|
||||
|
||||
timeout: 3,
|
||||
},
|
||||
|
||||
{
|
||||
match: {
|
||||
host: 'localhost'
|
||||
},
|
||||
|
||||
timeout: 4,
|
||||
},
|
||||
|
||||
{
|
||||
match: {},
|
||||
|
||||
timeout: 5
|
||||
}
|
||||
]
|
||||
|
||||
function getTimeout(uri) {
|
||||
const collection = new ProxyConfigCollection(proxyConfigs);
|
||||
return collection.configForUri(uri).timeout;
|
||||
}
|
||||
|
||||
context('http://localhost:5601', function () {
|
||||
it('defaults to the first matching timeout', function () {
|
||||
expect(getTimeout('http://localhost:5601')).to.be(3)
|
||||
});
|
||||
});
|
||||
|
||||
context('https://localhost:5601/.kibana', function () {
|
||||
it('defaults to the first matching timeout', function () {
|
||||
expect(getTimeout('https://localhost:5601/.kibana')).to.be(1);
|
||||
});
|
||||
});
|
||||
|
||||
context('http://localhost:5602', function () {
|
||||
it('defaults to the first matching timeout', function () {
|
||||
expect(getTimeout('http://localhost:5602')).to.be(4);
|
||||
});
|
||||
});
|
||||
|
||||
context('https://localhost:5602', function () {
|
||||
it('defaults to the first matching timeout', function () {
|
||||
expect(getTimeout('https://localhost:5602')).to.be(4);
|
||||
});
|
||||
});
|
||||
|
||||
context('http://localhost:5603', function () {
|
||||
it('defaults to the first matching timeout', function () {
|
||||
expect(getTimeout('http://localhost:5603')).to.be(4);
|
||||
});
|
||||
});
|
||||
|
||||
context('https://localhost:5603', function () {
|
||||
it('defaults to the first matching timeout', function () {
|
||||
expect(getTimeout('https://localhost:5603')).to.be(4);
|
||||
});
|
||||
});
|
||||
|
||||
context('https://localhost:5601/index', function () {
|
||||
it('defaults to the first matching timeout', function () {
|
||||
expect(getTimeout('https://localhost:5601/index')).to.be(2);
|
||||
});
|
||||
});
|
||||
|
||||
context('http://localhost:5601/index', function () {
|
||||
it('defaults to the first matching timeout', function () {
|
||||
expect(getTimeout('http://localhost:5601/index')).to.be(3);
|
||||
});
|
||||
});
|
||||
|
||||
context('https://localhost:5601/index/type', function () {
|
||||
it('defaults to the first matching timeout', function () {
|
||||
expect(getTimeout('https://localhost:5601/index/type')).to.be(2);
|
||||
});
|
||||
});
|
||||
|
||||
context('http://notlocalhost', function () {
|
||||
it('defaults to the first matching timeout', function () {
|
||||
expect(getTimeout('http://notlocalhost')).to.be(5);
|
||||
});
|
||||
});
|
||||
|
||||
context('collection with ssl config and root level verify:false', function () {
|
||||
function makeCollection() {
|
||||
return new ProxyConfigCollection([
|
||||
{
|
||||
match: { host: '*.internal.org' },
|
||||
ssl: { ca: ['path/to/ca'] }
|
||||
},
|
||||
{
|
||||
match: { host: '*' },
|
||||
ssl: { verify: false }
|
||||
}
|
||||
]);
|
||||
}
|
||||
|
||||
it('verifies for config that produces ssl agent', function () {
|
||||
const conf = makeCollection().configForUri('https://es.internal.org/_search');
|
||||
expect(conf).to.have.property('rejectUnauthorized', true);
|
||||
expect(conf.agent).to.be.an(HttpsAgent);
|
||||
});
|
||||
|
||||
it('disabled verification for * config', function () {
|
||||
const conf = makeCollection().configForUri('https://extenal.org/_search');
|
||||
expect(conf).to.have.property('rejectUnauthorized', false);
|
||||
expect(conf.agent).to.be(undefined);
|
||||
});
|
||||
});
|
||||
});
|
55
src/plugins/console/server/__tests__/wildcard_matcher.js
Normal file
55
src/plugins/console/server/__tests__/wildcard_matcher.js
Normal file
|
@ -0,0 +1,55 @@
|
|||
/* eslint-env mocha */
|
||||
import expect from 'expect.js'
|
||||
|
||||
import { WildcardMatcher } from '../wildcard_matcher'
|
||||
|
||||
function should(candidate, ...constructorArgs) {
|
||||
if (!new WildcardMatcher(...constructorArgs).match(candidate)) {
|
||||
throw new Error(`Expected pattern ${[...constructorArgs]} to match ${candidate}`);
|
||||
}
|
||||
}
|
||||
|
||||
function shouldNot(candidate, ...constructorArgs) {
|
||||
if (new WildcardMatcher(...constructorArgs).match(candidate)) {
|
||||
throw new Error(`Expected pattern ${[...constructorArgs]} to not match ${candidate}`);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
describe('WildcardMatcher', function () {
|
||||
context('pattern = *', function () {
|
||||
it('matches http', () => should('http', '*'));
|
||||
it('matches https', () => should('https', '*'));
|
||||
it('matches nothing', () => should('', '*'));
|
||||
it('does not match /', () => shouldNot('/', '*'));
|
||||
it('matches localhost', () => should('localhost', '*'));
|
||||
it('matches a path', () => should('/index/type/_search', '*'));
|
||||
|
||||
context('defaultValue = /', function () {
|
||||
it('matches /', () => should('/', '*', '/'));
|
||||
});
|
||||
});
|
||||
|
||||
context('pattern = http', function () {
|
||||
it('matches http', () => should('http', 'http'));
|
||||
it('does not match https', () => shouldNot('https', 'http'));
|
||||
it('does not match nothing', () => shouldNot('', 'http'));
|
||||
it('does not match localhost', () => shouldNot('localhost', 'http'));
|
||||
it('does not match a path', () => shouldNot('/index/type/_search', 'http'));
|
||||
});
|
||||
|
||||
context('pattern = 560{1..9}', function () {
|
||||
it('does not match http', () => shouldNot('http', '560{1..9}'));
|
||||
it('does not matches 5600', () => shouldNot('5600', '560{1..9}'));
|
||||
it('matches 5601', () => should('5601', '560{1..9}'));
|
||||
it('matches 5602', () => should('5602', '560{1..9}'));
|
||||
it('matches 5603', () => should('5603', '560{1..9}'));
|
||||
it('matches 5604', () => should('5604', '560{1..9}'));
|
||||
it('matches 5605', () => should('5605', '560{1..9}'));
|
||||
it('matches 5606', () => should('5606', '560{1..9}'));
|
||||
it('matches 5607', () => should('5607', '560{1..9}'));
|
||||
it('matches 5608', () => should('5608', '560{1..9}'));
|
||||
it('matches 5609', () => should('5609', '560{1..9}'));
|
||||
it('does not matches 5610', () => shouldNot('5610', '560{1..9}'));
|
||||
});
|
||||
});
|
70
src/plugins/console/server/proxy_config.js
Normal file
70
src/plugins/console/server/proxy_config.js
Normal file
|
@ -0,0 +1,70 @@
|
|||
import { memoize, values } from 'lodash'
|
||||
import { format as formatUrl } from 'url'
|
||||
import { Agent as HttpsAgent } from 'https'
|
||||
import { readFileSync } from 'fs'
|
||||
|
||||
import { WildcardMatcher } from './wildcard_matcher'
|
||||
|
||||
const makeHttpsAgent = memoize(
|
||||
opts => new HttpsAgent(opts),
|
||||
opts => JSON.stringify(opts)
|
||||
)
|
||||
|
||||
export class ProxyConfig {
|
||||
constructor(config) {
|
||||
config = Object.assign({}, config);
|
||||
|
||||
// -----
|
||||
// read "match" info
|
||||
// -----
|
||||
const rawMatches = Object.assign({}, config.match);
|
||||
this.id = formatUrl({
|
||||
protocol: rawMatches.protocol,
|
||||
hostname: rawMatches.host,
|
||||
port: rawMatches.port,
|
||||
pathname: rawMatches.path
|
||||
}) || '*';
|
||||
|
||||
this.matchers = {
|
||||
protocol: new WildcardMatcher(rawMatches.protocol),
|
||||
host: new WildcardMatcher(rawMatches.host),
|
||||
port: new WildcardMatcher(rawMatches.port),
|
||||
path: new WildcardMatcher(rawMatches.path, '/'),
|
||||
};
|
||||
|
||||
// -----
|
||||
// read config vars
|
||||
// -----
|
||||
this.timeout = config.timeout;
|
||||
this.sslAgent = this._makeSslAgent(config);
|
||||
}
|
||||
|
||||
_makeSslAgent(config) {
|
||||
const ssl = config.ssl || {};
|
||||
this.verifySsl = ssl.verify;
|
||||
|
||||
const sslAgentOpts = {
|
||||
ca: ssl.ca && ssl.ca.map(ca => readFileSync(ca)),
|
||||
cert: ssl.cert && readFileSync(ssl.cert),
|
||||
key: ssl.key && readFileSync(ssl.key),
|
||||
};
|
||||
|
||||
if (values(sslAgentOpts).filter(Boolean).length) {
|
||||
return new HttpsAgent(sslAgentOpts);
|
||||
}
|
||||
}
|
||||
|
||||
getForParsedUri({ protocol, hostname, port, pathname }) {
|
||||
let match = this.matchers.protocol.match(protocol.slice(0, -1));
|
||||
match = match && this.matchers.host.match(hostname);
|
||||
match = match && this.matchers.port.match(port);
|
||||
match = match && this.matchers.path.match(pathname);
|
||||
|
||||
if (!match) return {};
|
||||
return {
|
||||
timeout: this.timeout,
|
||||
rejectUnauthorized: this.sslAgent ? true : this.verifySsl,
|
||||
agent: protocol === 'https:' ? this.sslAgent : undefined
|
||||
};
|
||||
}
|
||||
}
|
17
src/plugins/console/server/proxy_config_collection.js
Normal file
17
src/plugins/console/server/proxy_config_collection.js
Normal file
|
@ -0,0 +1,17 @@
|
|||
import { defaultsDeep } from 'lodash'
|
||||
|
||||
import { ProxyConfig } from './proxy_config'
|
||||
import { parse as parseUrl } from 'url'
|
||||
|
||||
|
||||
export class ProxyConfigCollection {
|
||||
constructor(configs = []) {
|
||||
this.configs = configs.map(settings => new ProxyConfig(settings))
|
||||
}
|
||||
|
||||
configForUri(uri) {
|
||||
const parsedUri = parseUrl(uri);
|
||||
const settings = this.configs.map(config => config.getForParsedUri(parsedUri));
|
||||
return defaultsDeep({}, ...settings);
|
||||
}
|
||||
}
|
24
src/plugins/console/server/wildcard_matcher.js
Normal file
24
src/plugins/console/server/wildcard_matcher.js
Normal file
|
@ -0,0 +1,24 @@
|
|||
import { Minimatch } from 'minimatch'
|
||||
|
||||
export class WildcardMatcher {
|
||||
constructor(wildcardPattern, emptyVal) {
|
||||
this.emptyVal = emptyVal;
|
||||
this.pattern = String(wildcardPattern || '*');
|
||||
this.matcher = new Minimatch(this.pattern, {
|
||||
noglobstar: true,
|
||||
dot: true,
|
||||
nocase: true,
|
||||
matchBase: true,
|
||||
nocomment: true
|
||||
})
|
||||
}
|
||||
|
||||
match(candidate) {
|
||||
const empty = !candidate || candidate === this.emptyVal;
|
||||
if (empty && this.pattern === '*') {
|
||||
return true;
|
||||
}
|
||||
|
||||
return this.matcher.match(candidate || '')
|
||||
}
|
||||
}
|
28
tasks/setup_kibana.js
Normal file
28
tasks/setup_kibana.js
Normal file
|
@ -0,0 +1,28 @@
|
|||
const exec = require('child_process').execFileSync;
|
||||
const stat = require('fs').statSync;
|
||||
|
||||
const fromRoot = require('path').resolve.bind(null, __dirname, '../');
|
||||
|
||||
module.exports = function (grunt) {
|
||||
grunt.registerTask('setup_kibana', function () {
|
||||
const kbnDir = fromRoot('../kibana');
|
||||
const kbnGitDir = fromRoot('../kibana/.git');
|
||||
|
||||
try {
|
||||
if (stat(kbnGitDir).isDirectory()) {
|
||||
exec('git', ['pull', 'origin', 'master'], { cwd: kbnDir });
|
||||
} else {
|
||||
throw new Error(`${kbnGitDir} is not a directory??`);
|
||||
}
|
||||
} catch (error) {
|
||||
if (error.code === 'ENOENT') {
|
||||
exec('git', ['clone', 'https://github.com/elastic/kibana.git', kbnDir]);
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
exec('npm', ['prune'], { cwd: kbnDir });
|
||||
exec('npm', ['install'], { cwd: kbnDir });
|
||||
});
|
||||
};
|
Loading…
Add table
Add a link
Reference in a new issue