Skip to content

Commit ed28155

Browse files
authored
Implement interactiveSetup plugin server side functionality: setup layout (elastic#105222)
1 parent ff2a5a8 commit ed28155

22 files changed

+280
-25
lines changed

.eslintrc.js

+1
Original file line numberDiff line numberDiff line change
@@ -1573,6 +1573,7 @@ module.exports = {
15731573
files: [
15741574
'src/plugins/security_oss/**/*.{js,mjs,ts,tsx}',
15751575
'src/plugins/spaces_oss/**/*.{js,mjs,ts,tsx}',
1576+
'src/plugins/interactive_setup/**/*.{js,mjs,ts,tsx}',
15761577
'x-pack/plugins/encrypted_saved_objects/**/*.{js,mjs,ts,tsx}',
15771578
'x-pack/plugins/security/**/*.{js,mjs,ts,tsx}',
15781579
'x-pack/plugins/spaces/**/*.{js,mjs,ts,tsx}',

.github/CODEOWNERS

+2-2
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,7 @@
252252
/src/core/server/csp/ @elastic/kibana-security @elastic/kibana-core
253253
/src/plugins/security_oss/ @elastic/kibana-security
254254
/src/plugins/spaces_oss/ @elastic/kibana-security
255-
/src/plugins/user_setup/ @elastic/kibana-security
255+
/src/plugins/interactive_setup/ @elastic/kibana-security
256256
/test/security_functional/ @elastic/kibana-security
257257
/x-pack/plugins/spaces/ @elastic/kibana-security
258258
/x-pack/plugins/encrypted_saved_objects/ @elastic/kibana-security
@@ -351,7 +351,7 @@
351351
/x-pack/test/case_api_integration @elastic/security-threat-hunting
352352
/x-pack/plugins/lists @elastic/security-detections-response
353353

354-
## Security Solution sub teams - security-onboarding-and-lifecycle-mgt
354+
## Security Solution sub teams - security-onboarding-and-lifecycle-mgt
355355
/x-pack/plugins/security_solution/public/management/ @elastic/security-onboarding-and-lifecycle-mgt
356356
/x-pack/plugins/security_solution/public/common/lib/endpoint*/ @elastic/security-onboarding-and-lifecycle-mgt
357357
/x-pack/plugins/security_solution/public/common/components/endpoint/ @elastic/security-onboarding-and-lifecycle-mgt

.i18nrc.json

+1
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
"presentationUtil": "src/plugins/presentation_util",
3838
"indexPatternFieldEditor": "src/plugins/index_pattern_field_editor",
3939
"indexPatternManagement": "src/plugins/index_pattern_management",
40+
"interactiveSetup": "src/plugins/interactive_setup",
4041
"advancedSettings": "src/plugins/advanced_settings",
4142
"kibana_legacy": "src/plugins/kibana_legacy",
4243
"kibanaOverview": "src/plugins/kibana_overview",

docs/developer/plugin-list.asciidoc

+4-4
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,10 @@ for use in their own application.
136136
in Kibana, e.g. visualizations. It has the form of a flyout panel.
137137
138138
139+
|{kib-repo}blob/{branch}/src/plugins/interactive_setup/README.md[interactiveSetup]
140+
|The plugin provides UI and APIs for the interactive setup mode.
141+
142+
139143
|{kib-repo}blob/{branch}/src/plugins/kibana_legacy/README.md[kibanaLegacy]
140144
|This plugin contains several helpers and services to integrate pieces of the legacy Kibana app with the new Kibana platform.
141145
@@ -276,10 +280,6 @@ In general this plugin provides:
276280
|The Usage Collection Service defines a set of APIs for other plugins to report the usage of their features. At the same time, it provides necessary the APIs for other services (i.e.: telemetry, monitoring, ...) to consume that usage data.
277281
278282
279-
|{kib-repo}blob/{branch}/src/plugins/user_setup/README.md[userSetup]
280-
|The plugin provides UI and APIs for the interactive setup mode.
281-
282-
283283
|{kib-repo}blob/{branch}/src/plugins/vis_default_editor/README.md[visDefaultEditor]
284284
|The default editor is used in most primary visualizations, e.x. Area, Data table, Pie, etc.
285285
It acts as a container for a particular visualization and options tabs. Contains the default "Data" tab in public/components/sidebar/data_tab.tsx.

packages/kbn-optimizer/limits.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -117,4 +117,4 @@ pageLoadAssetSize:
117117
expressionImage: 19288
118118
expressionMetric: 22238
119119
expressionShape: 30033
120-
userSetup: 18532
120+
interactiveSetup: 18532
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
# `userSetup` plugin
1+
# `interactiveSetup` plugin
22

33
The plugin provides UI and APIs for the interactive setup mode.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License
4+
* 2.0 and the Server Side Public License, v 1; you may not use this file except
5+
* in compliance with, at your election, the Elastic License 2.0 or the Server
6+
* Side Public License, v 1.
7+
*/
8+
9+
/**
10+
* Describes current status of the Elasticsearch connection.
11+
*/
12+
export enum ElasticsearchConnectionStatus {
13+
/**
14+
* Indicates that Kibana hasn't figured out yet if existing Elasticsearch connection configuration is valid.
15+
*/
16+
Unknown = 'unknown',
17+
18+
/**
19+
* Indicates that current Elasticsearch connection configuration valid and sufficient.
20+
*/
21+
Configured = 'configured',
22+
23+
/**
24+
* Indicates that current Elasticsearch connection configuration isn't valid or not sufficient.
25+
*/
26+
NotConfigured = 'not-configured',
27+
}

src/plugins/user_setup/server/plugin.ts src/plugins/interactive_setup/common/index.ts

+2-9
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,5 @@
66
* Side Public License, v 1.
77
*/
88

9-
import type { CoreSetup, CoreStart, Plugin } from 'src/core/server';
10-
11-
export class UserSetupPlugin implements Plugin {
12-
public setup(core: CoreSetup) {}
13-
14-
public start(core: CoreStart) {}
15-
16-
public stop() {}
17-
}
9+
export type { InteractiveSetupViewState, EnrollmentToken } from './types';
10+
export { ElasticsearchConnectionStatus } from './elasticsearch_connection_status';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License
4+
* 2.0 and the Server Side Public License, v 1; you may not use this file except
5+
* in compliance with, at your election, the Elastic License 2.0 or the Server
6+
* Side Public License, v 1.
7+
*/
8+
9+
import type { ElasticsearchConnectionStatus } from './elasticsearch_connection_status';
10+
11+
/**
12+
* A set of state details that interactive setup view retrieves from the Kibana server.
13+
*/
14+
export interface InteractiveSetupViewState {
15+
/**
16+
* Current status of the Elasticsearch connection.
17+
*/
18+
elasticsearchConnectionStatus: ElasticsearchConnectionStatus;
19+
}
20+
21+
/**
22+
* The token that allows one to configure Kibana instance to communicate with an existing Elasticsearch cluster that
23+
* has security features enabled.
24+
*/
25+
export interface EnrollmentToken {
26+
/**
27+
* The version of the Elasticsearch node that generated this enrollment token.
28+
*/
29+
ver: string;
30+
31+
/**
32+
* An array of addresses in the form of `<hostname>:<port>` or `<ip_address>:<port>` where the Elasticsearch node is listening for HTTP connections.
33+
*/
34+
adr: readonly string[];
35+
36+
/**
37+
* The SHA-256 fingerprint of the CA certificate that is used to sign the certificate that the Elasticsearch node presents for HTTP over TLS connections.
38+
*/
39+
fgr: string;
40+
41+
/**
42+
* An Elasticsearch API key (not encoded) that can be used as credentials authorized to call the enrollment related APIs in Elasticsearch.
43+
*/
44+
key: string;
45+
}

src/plugins/user_setup/jest.config.js src/plugins/interactive_setup/jest.config.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,5 @@
99
module.exports = {
1010
preset: '@kbn/test',
1111
rootDir: '../../..',
12-
roots: ['<rootDir>/src/plugins/user_setup'],
12+
roots: ['<rootDir>/src/plugins/interactive_setup'],
1313
};
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
{
2-
"id": "userSetup",
2+
"id": "interactiveSetup",
33
"owner": {
44
"name": "Platform Security",
55
"githubTeam": "kibana-security"
66
},
77
"description": "This plugin provides UI and APIs for the interactive setup mode.",
88
"version": "8.0.0",
99
"kibanaVersion": "kibana",
10-
"configPath": ["userSetup"],
10+
"type": "preboot",
11+
"configPath": ["interactiveSetup"],
1112
"server": true,
1213
"ui": true
1314
}

src/plugins/user_setup/public/plugin.tsx src/plugins/interactive_setup/public/plugin.tsx

+3-2
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,14 @@ import React from 'react';
1010
import ReactDOM from 'react-dom';
1111

1212
import type { CoreSetup, CoreStart, Plugin } from 'src/core/public';
13+
1314
import { App } from './app';
1415

1516
export class UserSetupPlugin implements Plugin {
1617
public setup(core: CoreSetup) {
1718
core.application.register({
18-
id: 'userSetup',
19-
title: 'User Setup',
19+
id: 'interactiveSetup',
20+
title: 'Interactive Setup',
2021
chromeless: true,
2122
mount: (params) => {
2223
ReactDOM.render(<App />, params.element);

src/plugins/user_setup/server/index.ts src/plugins/interactive_setup/server/index.ts

+8-2
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,11 @@
77
*/
88

99
import type { TypeOf } from '@kbn/config-schema';
10-
import type { PluginConfigDescriptor } from 'src/core/server';
10+
import type {
11+
PluginConfigDescriptor,
12+
PluginInitializer,
13+
PluginInitializerContext,
14+
} from 'src/core/server';
1115

1216
import { ConfigSchema } from './config';
1317
import { UserSetupPlugin } from './plugin';
@@ -16,4 +20,6 @@ export const config: PluginConfigDescriptor<TypeOf<typeof ConfigSchema>> = {
1620
schema: ConfigSchema,
1721
};
1822

19-
export const plugin = () => new UserSetupPlugin();
23+
export const plugin: PluginInitializer<void, never> = (
24+
initializerContext: PluginInitializerContext
25+
) => new UserSetupPlugin(initializerContext);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License
4+
* 2.0 and the Server Side Public License, v 1; you may not use this file except
5+
* in compliance with, at your election, the Elastic License 2.0 or the Server
6+
* Side Public License, v 1.
7+
*/
8+
9+
import type { Subscription } from 'rxjs';
10+
11+
import type { TypeOf } from '@kbn/config-schema';
12+
import type { CorePreboot, Logger, PluginInitializerContext, PrebootPlugin } from 'src/core/server';
13+
14+
import { ElasticsearchConnectionStatus } from '../common';
15+
import type { ConfigSchema, ConfigType } from './config';
16+
import { defineRoutes } from './routes';
17+
18+
export class UserSetupPlugin implements PrebootPlugin {
19+
readonly #logger: Logger;
20+
21+
#configSubscription?: Subscription;
22+
#config?: ConfigType;
23+
readonly #getConfig = () => {
24+
if (!this.#config) {
25+
throw new Error('Config is not available.');
26+
}
27+
return this.#config;
28+
};
29+
30+
#elasticsearchConnectionStatus = ElasticsearchConnectionStatus.Unknown;
31+
readonly #getElasticsearchConnectionStatus = () => {
32+
return this.#elasticsearchConnectionStatus;
33+
};
34+
35+
constructor(private readonly initializerContext: PluginInitializerContext) {
36+
this.#logger = this.initializerContext.logger.get();
37+
}
38+
39+
public setup(core: CorePreboot) {
40+
this.#configSubscription = this.initializerContext.config
41+
.create<TypeOf<typeof ConfigSchema>>()
42+
.subscribe((config) => {
43+
this.#config = config;
44+
});
45+
46+
// We shouldn't activate interactive setup mode if we detect that user has already configured
47+
// Elasticsearch connection manually: either if Kibana system user credentials are specified or
48+
// user specified non-default host for the Elasticsearch.
49+
const shouldActiveSetupMode =
50+
!core.elasticsearch.config.credentialsSpecified &&
51+
core.elasticsearch.config.hosts.length === 1 &&
52+
core.elasticsearch.config.hosts[0] === 'http://localhost:9200';
53+
if (!shouldActiveSetupMode) {
54+
this.#logger.debug(
55+
'Interactive setup mode will not be activated since Elasticsearch connection is already configured.'
56+
);
57+
return;
58+
}
59+
60+
let completeSetup: (result: { shouldReloadConfig: boolean }) => void;
61+
core.preboot.holdSetupUntilResolved(
62+
'Validating Elasticsearch connection configuration…',
63+
new Promise((resolve) => {
64+
completeSetup = resolve;
65+
})
66+
);
67+
68+
// If preliminary check above indicates that user didn't alter default Elasticsearch connection
69+
// details, it doesn't mean Elasticsearch connection isn't configured. There is a chance that they
70+
// already disabled security features in Elasticsearch and everything should work by default.
71+
// We should check if we can connect to Elasticsearch with default configuration to know if we
72+
// need to activate interactive setup. This check can take some time, so we should register our
73+
// routes to let interactive setup UI to handle user requests until the check is complete.
74+
core.elasticsearch
75+
.createClient('ping')
76+
.asInternalUser.ping()
77+
.then(
78+
(pingResponse) => {
79+
if (pingResponse.body) {
80+
this.#logger.debug(
81+
'Kibana is already properly configured to connect to Elasticsearch. Interactive setup mode will not be activated.'
82+
);
83+
this.#elasticsearchConnectionStatus = ElasticsearchConnectionStatus.Configured;
84+
completeSetup({ shouldReloadConfig: false });
85+
} else {
86+
this.#logger.debug(
87+
'Kibana is not properly configured to connect to Elasticsearch. Interactive setup mode will be activated.'
88+
);
89+
this.#elasticsearchConnectionStatus = ElasticsearchConnectionStatus.NotConfigured;
90+
}
91+
},
92+
() => {
93+
// TODO: we should probably react differently to different errors. 401 - credentials aren't correct, etc.
94+
// Do we want to constantly ping ES if interactive mode UI isn't active? Just in case user runs Kibana and then
95+
// configure Elasticsearch so that it can eventually connect to it without any configuration changes?
96+
this.#elasticsearchConnectionStatus = ElasticsearchConnectionStatus.NotConfigured;
97+
}
98+
);
99+
100+
core.http.registerRoutes('', (router) => {
101+
defineRoutes({
102+
router,
103+
basePath: core.http.basePath,
104+
logger: this.#logger.get('routes'),
105+
getConfig: this.#getConfig.bind(this),
106+
getElasticsearchConnectionStatus: this.#getElasticsearchConnectionStatus.bind(this),
107+
});
108+
});
109+
}
110+
111+
public stop() {
112+
this.#logger.debug('Stopping plugin');
113+
114+
if (this.#configSubscription) {
115+
this.#configSubscription.unsubscribe();
116+
this.#configSubscription = undefined;
117+
}
118+
}
119+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License
4+
* 2.0 and the Server Side Public License, v 1; you may not use this file except
5+
* in compliance with, at your election, the Elastic License 2.0 or the Server
6+
* Side Public License, v 1.
7+
*/
8+
9+
import { schema } from '@kbn/config-schema';
10+
11+
import type { RouteDefinitionParams } from './';
12+
13+
/**
14+
* Defines routes to deal with Elasticsearch `enroll_kibana` APIs.
15+
*/
16+
export function defineEnrollRoutes({ router }: RouteDefinitionParams) {
17+
router.post(
18+
{
19+
path: '/internal/interactive_setup/enroll',
20+
validate: {
21+
body: schema.object({ token: schema.string() }),
22+
},
23+
options: { authRequired: false },
24+
},
25+
async (context, request, response) => {
26+
return response.forbidden({
27+
body: { message: `API is not implemented yet.` },
28+
});
29+
}
30+
);
31+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License
4+
* 2.0 and the Server Side Public License, v 1; you may not use this file except
5+
* in compliance with, at your election, the Elastic License 2.0 or the Server
6+
* Side Public License, v 1.
7+
*/
8+
9+
import type { IBasePath, IRouter, Logger } from 'src/core/server';
10+
11+
import type { ElasticsearchConnectionStatus } from '../../common';
12+
import type { ConfigType } from '../config';
13+
import { defineEnrollRoutes } from './enroll';
14+
15+
/**
16+
* Describes parameters used to define HTTP routes.
17+
*/
18+
export interface RouteDefinitionParams {
19+
readonly router: IRouter;
20+
readonly basePath: IBasePath;
21+
readonly logger: Logger;
22+
readonly getConfig: () => ConfigType;
23+
readonly getElasticsearchConnectionStatus: () => ElasticsearchConnectionStatus;
24+
}
25+
26+
export function defineRoutes(params: RouteDefinitionParams) {
27+
defineEnrollRoutes(params);
28+
}

0 commit comments

Comments
 (0)