* [console] Deprecate "proxyFilter" and "proxyConfig" on 8.x (#113555) * Change MAJOR_VERSION to 7.16.0
This commit is contained in:
parent
b47f88afb4
commit
cef53a981a
9
src/plugins/console/common/constants/index.ts
Normal file
9
src/plugins/console/common/constants/index.ts
Normal file
|
@ -0,0 +1,9 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
export { MAJOR_VERSION } from './plugin';
|
9
src/plugins/console/common/constants/plugin.ts
Normal file
9
src/plugins/console/common/constants/plugin.ts
Normal file
|
@ -0,0 +1,9 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
export const MAJOR_VERSION = '7.16.0';
|
|
@ -1,12 +1,14 @@
|
|||
{
|
||||
"id": "console",
|
||||
"version": "kibana",
|
||||
"version": "8.0.0",
|
||||
"kibanaVersion": "kibana",
|
||||
"server": true,
|
||||
"ui": true,
|
||||
"owner": {
|
||||
"name": "Stack Management",
|
||||
"githubTeam": "kibana-stack-management"
|
||||
},
|
||||
"configPath": ["console"],
|
||||
"requiredPlugins": ["devTools", "share"],
|
||||
"optionalPlugins": ["usageCollection", "home"],
|
||||
"requiredBundles": ["esUiShared", "kibanaReact", "kibanaUtils", "home"]
|
||||
|
|
|
@ -6,37 +6,70 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { SemVer } from 'semver';
|
||||
import { schema, TypeOf } from '@kbn/config-schema';
|
||||
import { PluginConfigDescriptor } from 'kibana/server';
|
||||
|
||||
export type ConfigType = TypeOf<typeof config>;
|
||||
import { MAJOR_VERSION } from '../common/constants';
|
||||
|
||||
export const config = schema.object(
|
||||
{
|
||||
enabled: schema.boolean({ defaultValue: true }),
|
||||
proxyFilter: schema.arrayOf(schema.string(), { defaultValue: ['.*'] }),
|
||||
ssl: schema.object({ verify: schema.boolean({ defaultValue: false }) }, {}),
|
||||
proxyConfig: schema.arrayOf(
|
||||
schema.object({
|
||||
match: schema.object({
|
||||
protocol: schema.string({ defaultValue: '*' }),
|
||||
host: schema.string({ defaultValue: '*' }),
|
||||
port: schema.string({ defaultValue: '*' }),
|
||||
path: schema.string({ defaultValue: '*' }),
|
||||
}),
|
||||
const kibanaVersion = new SemVer(MAJOR_VERSION);
|
||||
|
||||
timeout: schema.number(),
|
||||
ssl: schema.object(
|
||||
{
|
||||
verify: schema.boolean(),
|
||||
ca: schema.arrayOf(schema.string()),
|
||||
cert: schema.string(),
|
||||
key: schema.string(),
|
||||
},
|
||||
{ defaultValue: undefined }
|
||||
),
|
||||
const baseSettings = {
|
||||
enabled: schema.boolean({ defaultValue: true }),
|
||||
ssl: schema.object({ verify: schema.boolean({ defaultValue: false }) }, {}),
|
||||
};
|
||||
|
||||
// Settings only available in 7.x
|
||||
const deprecatedSettings = {
|
||||
proxyFilter: schema.arrayOf(schema.string(), { defaultValue: ['.*'] }),
|
||||
proxyConfig: schema.arrayOf(
|
||||
schema.object({
|
||||
match: schema.object({
|
||||
protocol: schema.string({ defaultValue: '*' }),
|
||||
host: schema.string({ defaultValue: '*' }),
|
||||
port: schema.string({ defaultValue: '*' }),
|
||||
path: schema.string({ defaultValue: '*' }),
|
||||
}),
|
||||
{ defaultValue: [] }
|
||||
),
|
||||
|
||||
timeout: schema.number(),
|
||||
ssl: schema.object(
|
||||
{
|
||||
verify: schema.boolean(),
|
||||
ca: schema.arrayOf(schema.string()),
|
||||
cert: schema.string(),
|
||||
key: schema.string(),
|
||||
},
|
||||
{ defaultValue: undefined }
|
||||
),
|
||||
}),
|
||||
{ defaultValue: [] }
|
||||
),
|
||||
};
|
||||
|
||||
const configSchema = schema.object(
|
||||
{
|
||||
...baseSettings,
|
||||
},
|
||||
{ defaultValue: undefined }
|
||||
);
|
||||
|
||||
const configSchema7x = schema.object(
|
||||
{
|
||||
...baseSettings,
|
||||
...deprecatedSettings,
|
||||
},
|
||||
{ defaultValue: undefined }
|
||||
);
|
||||
|
||||
export type ConfigType = TypeOf<typeof configSchema>;
|
||||
export type ConfigType7x = TypeOf<typeof configSchema7x>;
|
||||
|
||||
export const config: PluginConfigDescriptor<ConfigType | ConfigType7x> = {
|
||||
schema: kibanaVersion.major < 8 ? configSchema7x : configSchema,
|
||||
deprecations: ({ deprecate, unused }) => [
|
||||
deprecate('enabled', '8.0.0'),
|
||||
deprecate('proxyFilter', '8.0.0'),
|
||||
deprecate('proxyConfig', '8.0.0'),
|
||||
unused('ssl'),
|
||||
],
|
||||
};
|
||||
|
|
|
@ -6,16 +6,11 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { PluginConfigDescriptor, PluginInitializerContext } from 'kibana/server';
|
||||
import { PluginInitializerContext } from 'kibana/server';
|
||||
|
||||
import { ConfigType, config as configSchema } from './config';
|
||||
import { ConsoleServerPlugin } from './plugin';
|
||||
|
||||
export { ConsoleSetup, ConsoleStart } from './types';
|
||||
export { config } from './config';
|
||||
|
||||
export const plugin = (ctx: PluginInitializerContext) => new ConsoleServerPlugin(ctx);
|
||||
|
||||
export const config: PluginConfigDescriptor<ConfigType> = {
|
||||
deprecations: ({ deprecate, unused, rename }) => [deprecate('enabled', '8.0.0'), unused('ssl')],
|
||||
schema: configSchema,
|
||||
};
|
||||
|
|
|
@ -7,10 +7,11 @@
|
|||
*/
|
||||
|
||||
import { CoreSetup, Logger, Plugin, PluginInitializerContext } from 'kibana/server';
|
||||
import { SemVer } from 'semver';
|
||||
|
||||
import { ProxyConfigCollection } from './lib';
|
||||
import { SpecDefinitionsService, EsLegacyConfigService } from './services';
|
||||
import { ConfigType } from './config';
|
||||
import { ConfigType, ConfigType7x } from './config';
|
||||
|
||||
import { registerRoutes } from './routes';
|
||||
|
||||
|
@ -23,7 +24,7 @@ export class ConsoleServerPlugin implements Plugin<ConsoleSetup, ConsoleStart> {
|
|||
|
||||
esLegacyConfigService = new EsLegacyConfigService();
|
||||
|
||||
constructor(private readonly ctx: PluginInitializerContext<ConfigType>) {
|
||||
constructor(private readonly ctx: PluginInitializerContext<ConfigType | ConfigType7x>) {
|
||||
this.log = this.ctx.logger.get();
|
||||
}
|
||||
|
||||
|
@ -34,10 +35,17 @@ export class ConsoleServerPlugin implements Plugin<ConsoleSetup, ConsoleStart> {
|
|||
save: true,
|
||||
},
|
||||
}));
|
||||
|
||||
const kibanaVersion = new SemVer(this.ctx.env.packageInfo.version);
|
||||
const config = this.ctx.config.get();
|
||||
const globalConfig = this.ctx.config.legacy.get();
|
||||
const proxyPathFilters = config.proxyFilter.map((str: string) => new RegExp(str));
|
||||
|
||||
let pathFilters: RegExp[] | undefined;
|
||||
let proxyConfigCollection: ProxyConfigCollection | undefined;
|
||||
if (kibanaVersion.major < 8) {
|
||||
// "pathFilters" and "proxyConfig" are only used in 7.x
|
||||
pathFilters = (config as ConfigType7x).proxyFilter.map((str: string) => new RegExp(str));
|
||||
proxyConfigCollection = new ProxyConfigCollection((config as ConfigType7x).proxyConfig);
|
||||
}
|
||||
|
||||
this.esLegacyConfigService.setup(elasticsearch.legacy.config$);
|
||||
|
||||
|
@ -51,7 +59,6 @@ export class ConsoleServerPlugin implements Plugin<ConsoleSetup, ConsoleStart> {
|
|||
specDefinitionService: this.specDefinitionsService,
|
||||
},
|
||||
proxy: {
|
||||
proxyConfigCollection: new ProxyConfigCollection(config.proxyConfig),
|
||||
readLegacyESConfig: async (): Promise<ESConfigForProxy> => {
|
||||
const legacyConfig = await this.esLegacyConfigService.readConfig();
|
||||
return {
|
||||
|
@ -59,8 +66,11 @@ export class ConsoleServerPlugin implements Plugin<ConsoleSetup, ConsoleStart> {
|
|||
...legacyConfig,
|
||||
};
|
||||
},
|
||||
pathFilters: proxyPathFilters,
|
||||
// Deprecated settings (only used in 7.x):
|
||||
proxyConfigCollection,
|
||||
pathFilters,
|
||||
},
|
||||
kibanaVersion,
|
||||
});
|
||||
|
||||
return {
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
import { Agent, IncomingMessage } from 'http';
|
||||
import * as url from 'url';
|
||||
import { pick, trimStart, trimEnd } from 'lodash';
|
||||
import { SemVer } from 'semver';
|
||||
|
||||
import { KibanaRequest, RequestHandler } from 'kibana/server';
|
||||
|
||||
|
@ -58,17 +59,22 @@ function filterHeaders(originalHeaders: object, headersToKeep: string[]): object
|
|||
function getRequestConfig(
|
||||
headers: object,
|
||||
esConfig: ESConfigForProxy,
|
||||
proxyConfigCollection: ProxyConfigCollection,
|
||||
uri: string
|
||||
uri: string,
|
||||
kibanaVersion: SemVer,
|
||||
proxyConfigCollection?: ProxyConfigCollection
|
||||
): { agent: Agent; timeout: number; headers: object; rejectUnauthorized?: boolean } {
|
||||
const filteredHeaders = filterHeaders(headers, esConfig.requestHeadersWhitelist);
|
||||
const newHeaders = setHeaders(filteredHeaders, esConfig.customHeaders);
|
||||
|
||||
if (proxyConfigCollection.hasConfig()) {
|
||||
return {
|
||||
...proxyConfigCollection.configForUri(uri),
|
||||
headers: newHeaders,
|
||||
};
|
||||
if (kibanaVersion.major < 8) {
|
||||
// In 7.x we still support the proxyConfig setting defined in kibana.yml
|
||||
// From 8.x we don't support it anymore so we don't try to read it here.
|
||||
if (proxyConfigCollection!.hasConfig()) {
|
||||
return {
|
||||
...proxyConfigCollection!.configForUri(uri),
|
||||
headers: newHeaders,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
|
@ -106,18 +112,23 @@ export const createHandler =
|
|||
({
|
||||
log,
|
||||
proxy: { readLegacyESConfig, pathFilters, proxyConfigCollection },
|
||||
kibanaVersion,
|
||||
}: RouteDependencies): RequestHandler<unknown, Query, Body> =>
|
||||
async (ctx, request, response) => {
|
||||
const { body, query } = request;
|
||||
const { path, method } = query;
|
||||
|
||||
if (!pathFilters.some((re) => re.test(path))) {
|
||||
return response.forbidden({
|
||||
body: `Error connecting to '${path}':\n\nUnable to send requests to that path.`,
|
||||
headers: {
|
||||
'Content-Type': 'text/plain',
|
||||
},
|
||||
});
|
||||
if (kibanaVersion.major < 8) {
|
||||
// The "console.proxyFilter" setting in kibana.yaml has been deprecated in 8.x
|
||||
// We only read it on the 7.x branch
|
||||
if (!pathFilters!.some((re) => re.test(path))) {
|
||||
return response.forbidden({
|
||||
body: `Error connecting to '${path}':\n\nUnable to send requests to that path.`,
|
||||
headers: {
|
||||
'Content-Type': 'text/plain',
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const legacyConfig = await readLegacyESConfig();
|
||||
|
@ -134,8 +145,9 @@ export const createHandler =
|
|||
const { timeout, agent, headers, rejectUnauthorized } = getRequestConfig(
|
||||
request.headers,
|
||||
legacyConfig,
|
||||
proxyConfigCollection,
|
||||
uri.toString()
|
||||
uri.toString(),
|
||||
kibanaVersion,
|
||||
proxyConfigCollection
|
||||
);
|
||||
|
||||
const requestHeaders = {
|
||||
|
|
|
@ -5,28 +5,41 @@
|
|||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
import { SemVer } from 'semver';
|
||||
|
||||
jest.mock('../../../../lib/proxy_request', () => ({
|
||||
proxyRequest: jest.fn(),
|
||||
}));
|
||||
|
||||
import { duration } from 'moment';
|
||||
import { MAJOR_VERSION } from '../../../../../common/constants';
|
||||
import { ProxyConfigCollection } from '../../../../lib';
|
||||
import { RouteDependencies, ProxyDependencies } from '../../../../routes';
|
||||
import { EsLegacyConfigService, SpecDefinitionsService } from '../../../../services';
|
||||
import { coreMock, httpServiceMock } from '../../../../../../../core/server/mocks';
|
||||
|
||||
const defaultProxyValue = Object.freeze({
|
||||
readLegacyESConfig: async () => ({
|
||||
requestTimeout: duration(30000),
|
||||
customHeaders: {},
|
||||
requestHeadersWhitelist: [],
|
||||
hosts: ['http://localhost:9200'],
|
||||
}),
|
||||
pathFilters: [/.*/],
|
||||
proxyConfigCollection: new ProxyConfigCollection([]),
|
||||
const kibanaVersion = new SemVer(MAJOR_VERSION);
|
||||
|
||||
const readLegacyESConfig = async () => ({
|
||||
requestTimeout: duration(30000),
|
||||
customHeaders: {},
|
||||
requestHeadersWhitelist: [],
|
||||
hosts: ['http://localhost:9200'],
|
||||
});
|
||||
|
||||
let defaultProxyValue = Object.freeze({
|
||||
readLegacyESConfig,
|
||||
});
|
||||
|
||||
if (kibanaVersion.major < 8) {
|
||||
// In 7.x we still support the "pathFilter" and "proxyConfig" kibana.yml settings
|
||||
defaultProxyValue = Object.freeze({
|
||||
readLegacyESConfig,
|
||||
pathFilters: [/.*/],
|
||||
proxyConfigCollection: new ProxyConfigCollection([]),
|
||||
});
|
||||
}
|
||||
|
||||
interface MockDepsArgument extends Partial<Omit<RouteDependencies, 'proxy'>> {
|
||||
proxy?: Partial<ProxyDependencies>;
|
||||
}
|
||||
|
@ -51,5 +64,6 @@ export const getProxyRouteHandlerDeps = ({
|
|||
}
|
||||
: defaultProxyValue,
|
||||
log,
|
||||
kibanaVersion,
|
||||
};
|
||||
};
|
||||
|
|
|
@ -5,14 +5,17 @@
|
|||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
import { SemVer } from 'semver';
|
||||
|
||||
import { kibanaResponseFactory } from '../../../../../../../core/server';
|
||||
import { getProxyRouteHandlerDeps } from './mocks';
|
||||
import { createResponseStub } from './stubs';
|
||||
import { getProxyRouteHandlerDeps } from './mocks'; // import need to come first
|
||||
import { createResponseStub } from './stubs'; // import needs to come first
|
||||
import { MAJOR_VERSION } from '../../../../../common/constants';
|
||||
import * as requestModule from '../../../../lib/proxy_request';
|
||||
|
||||
import { createHandler } from './create_handler';
|
||||
|
||||
const kibanaVersion = new SemVer(MAJOR_VERSION);
|
||||
|
||||
describe('Console Proxy Route', () => {
|
||||
let handler: ReturnType<typeof createHandler>;
|
||||
|
||||
|
@ -21,58 +24,71 @@ describe('Console Proxy Route', () => {
|
|||
});
|
||||
|
||||
describe('params', () => {
|
||||
describe('pathFilters', () => {
|
||||
describe('no matches', () => {
|
||||
it('rejects with 403', async () => {
|
||||
handler = createHandler(
|
||||
getProxyRouteHandlerDeps({ proxy: { pathFilters: [/^\/foo\//, /^\/bar\//] } })
|
||||
);
|
||||
if (kibanaVersion.major < 8) {
|
||||
describe('pathFilters', () => {
|
||||
describe('no matches', () => {
|
||||
it('rejects with 403', async () => {
|
||||
handler = createHandler(
|
||||
getProxyRouteHandlerDeps({
|
||||
proxy: { pathFilters: [/^\/foo\//, /^\/bar\//] },
|
||||
})
|
||||
);
|
||||
|
||||
const { status } = await handler(
|
||||
{} as any,
|
||||
{ query: { method: 'POST', path: '/baz/id' } } as any,
|
||||
kibanaResponseFactory
|
||||
);
|
||||
const { status } = await handler(
|
||||
{} as any,
|
||||
{ query: { method: 'POST', path: '/baz/id' } } as any,
|
||||
kibanaResponseFactory
|
||||
);
|
||||
|
||||
expect(status).toBe(403);
|
||||
expect(status).toBe(403);
|
||||
});
|
||||
});
|
||||
|
||||
describe('one match', () => {
|
||||
it('allows the request', async () => {
|
||||
handler = createHandler(
|
||||
getProxyRouteHandlerDeps({
|
||||
proxy: { pathFilters: [/^\/foo\//, /^\/bar\//] },
|
||||
})
|
||||
);
|
||||
|
||||
(requestModule.proxyRequest as jest.Mock).mockResolvedValue(createResponseStub('foo'));
|
||||
|
||||
const { status } = await handler(
|
||||
{} as any,
|
||||
{ headers: {}, query: { method: 'POST', path: '/foo/id' } } as any,
|
||||
kibanaResponseFactory
|
||||
);
|
||||
|
||||
expect(status).toBe(200);
|
||||
expect((requestModule.proxyRequest as jest.Mock).mock.calls.length).toBe(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('all match', () => {
|
||||
it('allows the request', async () => {
|
||||
handler = createHandler(
|
||||
getProxyRouteHandlerDeps({ proxy: { pathFilters: [/^\/foo\//] } })
|
||||
);
|
||||
|
||||
(requestModule.proxyRequest as jest.Mock).mockResolvedValue(createResponseStub('foo'));
|
||||
|
||||
const { status } = await handler(
|
||||
{} as any,
|
||||
{ headers: {}, query: { method: 'GET', path: '/foo/id' } } as any,
|
||||
kibanaResponseFactory
|
||||
);
|
||||
|
||||
expect(status).toBe(200);
|
||||
expect((requestModule.proxyRequest as jest.Mock).mock.calls.length).toBe(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('one match', () => {
|
||||
it('allows the request', async () => {
|
||||
handler = createHandler(
|
||||
getProxyRouteHandlerDeps({ proxy: { pathFilters: [/^\/foo\//, /^\/bar\//] } })
|
||||
);
|
||||
|
||||
(requestModule.proxyRequest as jest.Mock).mockResolvedValue(createResponseStub('foo'));
|
||||
|
||||
const { status } = await handler(
|
||||
{} as any,
|
||||
{ headers: {}, query: { method: 'POST', path: '/foo/id' } } as any,
|
||||
kibanaResponseFactory
|
||||
);
|
||||
|
||||
expect(status).toBe(200);
|
||||
expect((requestModule.proxyRequest as jest.Mock).mock.calls.length).toBe(1);
|
||||
});
|
||||
} else {
|
||||
// jest requires to have at least one test in the file
|
||||
test('dummy required test', () => {
|
||||
expect(true).toBe(true);
|
||||
});
|
||||
describe('all match', () => {
|
||||
it('allows the request', async () => {
|
||||
handler = createHandler(
|
||||
getProxyRouteHandlerDeps({ proxy: { pathFilters: [/^\/foo\//] } })
|
||||
);
|
||||
|
||||
(requestModule.proxyRequest as jest.Mock).mockResolvedValue(createResponseStub('foo'));
|
||||
|
||||
const { status } = await handler(
|
||||
{} as any,
|
||||
{ headers: {}, query: { method: 'GET', path: '/foo/id' } } as any,
|
||||
kibanaResponseFactory
|
||||
);
|
||||
|
||||
expect(status).toBe(200);
|
||||
expect((requestModule.proxyRequest as jest.Mock).mock.calls.length).toBe(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
*/
|
||||
|
||||
import { IRouter, Logger } from 'kibana/server';
|
||||
import { SemVer } from 'semver';
|
||||
|
||||
import { EsLegacyConfigService, SpecDefinitionsService } from '../services';
|
||||
import { ESConfigForProxy } from '../types';
|
||||
|
@ -18,8 +19,8 @@ import { registerSpecDefinitionsRoute } from './api/console/spec_definitions';
|
|||
|
||||
export interface ProxyDependencies {
|
||||
readLegacyESConfig: () => Promise<ESConfigForProxy>;
|
||||
pathFilters: RegExp[];
|
||||
proxyConfigCollection: ProxyConfigCollection;
|
||||
pathFilters?: RegExp[]; // Only present in 7.x
|
||||
proxyConfigCollection?: ProxyConfigCollection; // Only present in 7.x
|
||||
}
|
||||
|
||||
export interface RouteDependencies {
|
||||
|
@ -30,6 +31,7 @@ export interface RouteDependencies {
|
|||
esLegacyConfigService: EsLegacyConfigService;
|
||||
specDefinitionService: SpecDefinitionsService;
|
||||
};
|
||||
kibanaVersion: SemVer;
|
||||
}
|
||||
|
||||
export const registerRoutes = (dependencies: RouteDependencies) => {
|
||||
|
|
Loading…
Reference in a new issue