Cumulative set of the preboot stage adjustments (#108514)
This commit is contained in:
parent
8deaa573de
commit
3a0f209bde
|
@ -14,5 +14,6 @@ export declare type ElasticsearchClientConfig = Pick<ElasticsearchConfig, 'custo
|
||||||
requestTimeout?: ElasticsearchConfig['requestTimeout'] | ClientOptions['requestTimeout'];
|
requestTimeout?: ElasticsearchConfig['requestTimeout'] | ClientOptions['requestTimeout'];
|
||||||
ssl?: Partial<ElasticsearchConfig['ssl']>;
|
ssl?: Partial<ElasticsearchConfig['ssl']>;
|
||||||
keepAlive?: boolean;
|
keepAlive?: boolean;
|
||||||
|
caFingerprint?: ClientOptions['caFingerprint'];
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||||
|
|
||||||
|
[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [HttpServicePreboot](./kibana-plugin-core-server.httpservicepreboot.md) > [getServerInfo](./kibana-plugin-core-server.httpservicepreboot.getserverinfo.md)
|
||||||
|
|
||||||
|
## HttpServicePreboot.getServerInfo property
|
||||||
|
|
||||||
|
Provides common [information](./kibana-plugin-core-server.httpserverinfo.md) about the running preboot http server.
|
||||||
|
|
||||||
|
<b>Signature:</b>
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
getServerInfo: () => HttpServerInfo;
|
||||||
|
```
|
|
@ -73,6 +73,7 @@ httpPreboot.registerRoutes('my-plugin', (router) => {
|
||||||
| Property | Type | Description |
|
| Property | Type | Description |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| [basePath](./kibana-plugin-core-server.httpservicepreboot.basepath.md) | <code>IBasePath</code> | Access or manipulate the Kibana base path See [IBasePath](./kibana-plugin-core-server.ibasepath.md)<!-- -->. |
|
| [basePath](./kibana-plugin-core-server.httpservicepreboot.basepath.md) | <code>IBasePath</code> | Access or manipulate the Kibana base path See [IBasePath](./kibana-plugin-core-server.ibasepath.md)<!-- -->. |
|
||||||
|
| [getServerInfo](./kibana-plugin-core-server.httpservicepreboot.getserverinfo.md) | <code>() => HttpServerInfo</code> | Provides common [information](./kibana-plugin-core-server.httpserverinfo.md) about the running preboot http server. |
|
||||||
|
|
||||||
## Methods
|
## Methods
|
||||||
|
|
||||||
|
|
|
@ -137,7 +137,7 @@ describe('CoreApp', () => {
|
||||||
mockResponseFactory
|
mockResponseFactory
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(mockResponseFactory.renderAnonymousCoreApp).toHaveBeenCalled();
|
expect(mockResponseFactory.renderCoreApp).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -64,7 +64,7 @@ export class CoreApp {
|
||||||
httpResources: corePreboot.httpResources.createRegistrar(router),
|
httpResources: corePreboot.httpResources.createRegistrar(router),
|
||||||
router,
|
router,
|
||||||
uiPlugins,
|
uiPlugins,
|
||||||
onResourceNotFound: (res) => res.renderAnonymousCoreApp(),
|
onResourceNotFound: (res) => res.renderCoreApp(),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -163,6 +163,12 @@ describe('parseClientOptions', () => {
|
||||||
]
|
]
|
||||||
`);
|
`);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('`caFingerprint` option', () => {
|
||||||
|
const options = parseClientOptions(createConfig({ caFingerprint: 'ab:cd:ef' }), false);
|
||||||
|
|
||||||
|
expect(options.caFingerprint).toBe('ab:cd:ef');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('authorization', () => {
|
describe('authorization', () => {
|
||||||
|
|
|
@ -35,6 +35,7 @@ export type ElasticsearchClientConfig = Pick<
|
||||||
requestTimeout?: ElasticsearchConfig['requestTimeout'] | ClientOptions['requestTimeout'];
|
requestTimeout?: ElasticsearchConfig['requestTimeout'] | ClientOptions['requestTimeout'];
|
||||||
ssl?: Partial<ElasticsearchConfig['ssl']>;
|
ssl?: Partial<ElasticsearchConfig['ssl']>;
|
||||||
keepAlive?: boolean;
|
keepAlive?: boolean;
|
||||||
|
caFingerprint?: ClientOptions['caFingerprint'];
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -96,6 +97,10 @@ export function parseClientOptions(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (config.caFingerprint != null) {
|
||||||
|
clientOptions.caFingerprint = config.caFingerprint;
|
||||||
|
}
|
||||||
|
|
||||||
return clientOptions;
|
return clientOptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -88,6 +88,7 @@ const createInternalPrebootContractMock = () => {
|
||||||
csp: CspConfig.DEFAULT,
|
csp: CspConfig.DEFAULT,
|
||||||
externalUrl: ExternalUrlConfig.DEFAULT,
|
externalUrl: ExternalUrlConfig.DEFAULT,
|
||||||
auth: createAuthMock(),
|
auth: createAuthMock(),
|
||||||
|
getServerInfo: jest.fn(),
|
||||||
};
|
};
|
||||||
return mock;
|
return mock;
|
||||||
};
|
};
|
||||||
|
@ -98,6 +99,7 @@ const createPrebootContractMock = () => {
|
||||||
const mock: HttpServicePrebootMock = {
|
const mock: HttpServicePrebootMock = {
|
||||||
registerRoutes: internalMock.registerRoutes,
|
registerRoutes: internalMock.registerRoutes,
|
||||||
basePath: createBasePathMock(),
|
basePath: createBasePathMock(),
|
||||||
|
getServerInfo: jest.fn(),
|
||||||
};
|
};
|
||||||
|
|
||||||
return mock;
|
return mock;
|
||||||
|
|
|
@ -379,6 +379,7 @@ test('returns `preboot` http server contract on preboot', async () => {
|
||||||
auth: Symbol('auth'),
|
auth: Symbol('auth'),
|
||||||
basePath: Symbol('basePath'),
|
basePath: Symbol('basePath'),
|
||||||
csp: Symbol('csp'),
|
csp: Symbol('csp'),
|
||||||
|
getServerInfo: jest.fn(),
|
||||||
};
|
};
|
||||||
|
|
||||||
mockHttpServer.mockImplementation(() => ({
|
mockHttpServer.mockImplementation(() => ({
|
||||||
|
@ -397,6 +398,7 @@ test('returns `preboot` http server contract on preboot', async () => {
|
||||||
registerRouteHandlerContext: expect.any(Function),
|
registerRouteHandlerContext: expect.any(Function),
|
||||||
registerRoutes: expect.any(Function),
|
registerRoutes: expect.any(Function),
|
||||||
registerStaticDir: expect.any(Function),
|
registerStaticDir: expect.any(Function),
|
||||||
|
getServerInfo: expect.any(Function),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -128,6 +128,7 @@ export class HttpService
|
||||||
|
|
||||||
prebootSetup.registerRouterAfterListening(router);
|
prebootSetup.registerRouterAfterListening(router);
|
||||||
},
|
},
|
||||||
|
getServerInfo: prebootSetup.getServerInfo,
|
||||||
};
|
};
|
||||||
|
|
||||||
return this.internalPreboot;
|
return this.internalPreboot;
|
||||||
|
|
|
@ -142,6 +142,11 @@ export interface HttpServicePreboot {
|
||||||
* See {@link IBasePath}.
|
* See {@link IBasePath}.
|
||||||
*/
|
*/
|
||||||
basePath: IBasePath;
|
basePath: IBasePath;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides common {@link HttpServerInfo | information} about the running preboot http server.
|
||||||
|
*/
|
||||||
|
getServerInfo: () => HttpServerInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
|
@ -155,6 +160,7 @@ export interface InternalHttpServicePreboot
|
||||||
| 'registerStaticDir'
|
| 'registerStaticDir'
|
||||||
| 'registerRouteHandlerContext'
|
| 'registerRouteHandlerContext'
|
||||||
| 'server'
|
| 'server'
|
||||||
|
| 'getServerInfo'
|
||||||
> {
|
> {
|
||||||
registerRoutes(path: string, callback: (router: IRouter) => void): void;
|
registerRoutes(path: string, callback: (router: IRouter) => void): void;
|
||||||
}
|
}
|
||||||
|
|
|
@ -115,6 +115,7 @@ export function createPluginPrebootSetupContext(
|
||||||
http: {
|
http: {
|
||||||
registerRoutes: deps.http.registerRoutes,
|
registerRoutes: deps.http.registerRoutes,
|
||||||
basePath: deps.http.basePath,
|
basePath: deps.http.basePath,
|
||||||
|
getServerInfo: deps.http.getServerInfo,
|
||||||
},
|
},
|
||||||
preboot: {
|
preboot: {
|
||||||
isSetupOnHold: deps.preboot.isSetupOnHold,
|
isSetupOnHold: deps.preboot.isSetupOnHold,
|
||||||
|
|
|
@ -807,6 +807,7 @@ export type ElasticsearchClientConfig = Pick<ElasticsearchConfig, 'customHeaders
|
||||||
requestTimeout?: ElasticsearchConfig['requestTimeout'] | ClientOptions['requestTimeout'];
|
requestTimeout?: ElasticsearchConfig['requestTimeout'] | ClientOptions['requestTimeout'];
|
||||||
ssl?: Partial<ElasticsearchConfig['ssl']>;
|
ssl?: Partial<ElasticsearchConfig['ssl']>;
|
||||||
keepAlive?: boolean;
|
keepAlive?: boolean;
|
||||||
|
caFingerprint?: ClientOptions['caFingerprint'];
|
||||||
};
|
};
|
||||||
|
|
||||||
// @public
|
// @public
|
||||||
|
@ -1003,6 +1004,7 @@ export interface HttpServerInfo {
|
||||||
// @public
|
// @public
|
||||||
export interface HttpServicePreboot {
|
export interface HttpServicePreboot {
|
||||||
basePath: IBasePath;
|
basePath: IBasePath;
|
||||||
|
getServerInfo: () => HttpServerInfo;
|
||||||
registerRoutes(path: string, callback: (router: IRouter) => void): void;
|
registerRoutes(path: string, callback: (router: IRouter) => void): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -308,7 +308,7 @@ describe('ElasticsearchService', () => {
|
||||||
mockEnrollClient.asScoped.mockReturnValue(mockScopedClusterClient);
|
mockEnrollClient.asScoped.mockReturnValue(mockScopedClusterClient);
|
||||||
|
|
||||||
await expect(
|
await expect(
|
||||||
setupContract.enroll({ apiKey: 'apiKey', hosts: ['host1'] })
|
setupContract.enroll({ apiKey: 'apiKey', hosts: ['host1'], caFingerprint: 'DE:AD:BE:EF' })
|
||||||
).rejects.toMatchInlineSnapshot(`[ResponseError: {"message":"oh no"}]`);
|
).rejects.toMatchInlineSnapshot(`[ResponseError: {"message":"oh no"}]`);
|
||||||
|
|
||||||
expect(mockEnrollClient.asScoped).toHaveBeenCalledTimes(1);
|
expect(mockEnrollClient.asScoped).toHaveBeenCalledTimes(1);
|
||||||
|
@ -327,7 +327,11 @@ describe('ElasticsearchService', () => {
|
||||||
mockEnrollClient.asScoped.mockReturnValue(mockScopedClusterClient);
|
mockEnrollClient.asScoped.mockReturnValue(mockScopedClusterClient);
|
||||||
|
|
||||||
await expect(
|
await expect(
|
||||||
setupContract.enroll({ apiKey: 'apiKey', hosts: ['host1', 'host2'] })
|
setupContract.enroll({
|
||||||
|
apiKey: 'apiKey',
|
||||||
|
hosts: ['host1', 'host2'],
|
||||||
|
caFingerprint: 'DE:AD:BE:EF',
|
||||||
|
})
|
||||||
).rejects.toMatchInlineSnapshot(`[Error: Unable to connect to any of the provided hosts.]`);
|
).rejects.toMatchInlineSnapshot(`[Error: Unable to connect to any of the provided hosts.]`);
|
||||||
|
|
||||||
expect(mockEnrollClient.close).toHaveBeenCalledTimes(2);
|
expect(mockEnrollClient.close).toHaveBeenCalledTimes(2);
|
||||||
|
@ -351,7 +355,7 @@ describe('ElasticsearchService', () => {
|
||||||
);
|
);
|
||||||
|
|
||||||
await expect(
|
await expect(
|
||||||
setupContract.enroll({ apiKey: 'apiKey', hosts: ['host1'] })
|
setupContract.enroll({ apiKey: 'apiKey', hosts: ['host1'], caFingerprint: 'DE:AD:BE:EF' })
|
||||||
).rejects.toMatchInlineSnapshot(`[ResponseError: {"message":"oh no"}]`);
|
).rejects.toMatchInlineSnapshot(`[ResponseError: {"message":"oh no"}]`);
|
||||||
|
|
||||||
expect(mockEnrollClient.asScoped).toHaveBeenCalledTimes(1);
|
expect(mockEnrollClient.asScoped).toHaveBeenCalledTimes(1);
|
||||||
|
@ -404,7 +408,11 @@ some weird+ca/with
|
||||||
`;
|
`;
|
||||||
|
|
||||||
await expect(
|
await expect(
|
||||||
setupContract.enroll({ apiKey: 'apiKey', hosts: ['host1', 'host2'] })
|
setupContract.enroll({
|
||||||
|
apiKey: 'apiKey',
|
||||||
|
hosts: ['host1', 'host2'],
|
||||||
|
caFingerprint: 'DE:AD:BE:EF',
|
||||||
|
})
|
||||||
).resolves.toEqual({
|
).resolves.toEqual({
|
||||||
ca: expectedCa,
|
ca: expectedCa,
|
||||||
host: 'host2',
|
host: 'host2',
|
||||||
|
@ -417,14 +425,17 @@ some weird+ca/with
|
||||||
// Check that we created clients with the right parameters
|
// Check that we created clients with the right parameters
|
||||||
expect(mockElasticsearchPreboot.createClient).toHaveBeenCalledTimes(3);
|
expect(mockElasticsearchPreboot.createClient).toHaveBeenCalledTimes(3);
|
||||||
expect(mockElasticsearchPreboot.createClient).toHaveBeenCalledWith('enroll', {
|
expect(mockElasticsearchPreboot.createClient).toHaveBeenCalledWith('enroll', {
|
||||||
|
caFingerprint: 'DE:AD:BE:EF',
|
||||||
hosts: ['host1'],
|
hosts: ['host1'],
|
||||||
ssl: { verificationMode: 'none' },
|
ssl: { verificationMode: 'none' },
|
||||||
});
|
});
|
||||||
expect(mockElasticsearchPreboot.createClient).toHaveBeenCalledWith('enroll', {
|
expect(mockElasticsearchPreboot.createClient).toHaveBeenCalledWith('enroll', {
|
||||||
|
caFingerprint: 'DE:AD:BE:EF',
|
||||||
hosts: ['host2'],
|
hosts: ['host2'],
|
||||||
ssl: { verificationMode: 'none' },
|
ssl: { verificationMode: 'none' },
|
||||||
});
|
});
|
||||||
expect(mockElasticsearchPreboot.createClient).toHaveBeenCalledWith('authenticate', {
|
expect(mockElasticsearchPreboot.createClient).toHaveBeenCalledWith('authenticate', {
|
||||||
|
caFingerprint: 'DE:AD:BE:EF',
|
||||||
hosts: ['host2'],
|
hosts: ['host2'],
|
||||||
serviceAccountToken: 'some-value',
|
serviceAccountToken: 'some-value',
|
||||||
ssl: { certificateAuthorities: [expectedCa] },
|
ssl: { certificateAuthorities: [expectedCa] },
|
||||||
|
|
|
@ -34,9 +34,7 @@ import { getDetailedErrorMessage } from './errors';
|
||||||
interface EnrollParameters {
|
interface EnrollParameters {
|
||||||
apiKey: string;
|
apiKey: string;
|
||||||
hosts: string[];
|
hosts: string[];
|
||||||
// TODO: Integrate fingerprint check as soon core supports this new option:
|
caFingerprint: string;
|
||||||
// https://github.com/elastic/kibana/pull/108514
|
|
||||||
caFingerprint?: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ElasticsearchServiceSetupDeps {
|
export interface ElasticsearchServiceSetupDeps {
|
||||||
|
@ -141,10 +139,12 @@ export class ElasticsearchService {
|
||||||
* @param apiKey The ApiKey to use to authenticate Kibana enrollment request.
|
* @param apiKey The ApiKey to use to authenticate Kibana enrollment request.
|
||||||
* @param hosts The list of Elasticsearch node addresses to enroll with. The addresses are supposed
|
* @param hosts The list of Elasticsearch node addresses to enroll with. The addresses are supposed
|
||||||
* to point to exactly same Elasticsearch node, potentially available via different network interfaces.
|
* to point to exactly same Elasticsearch node, potentially available via different network interfaces.
|
||||||
|
* @param caFingerprint The fingerprint of the root CA certificate that is supposed to sign certificate presented by
|
||||||
|
* the Elasticsearch node we're enrolling with. Should be in a form of a hex colon-delimited string in upper case.
|
||||||
*/
|
*/
|
||||||
private async enroll(
|
private async enroll(
|
||||||
elasticsearch: ElasticsearchServicePreboot,
|
elasticsearch: ElasticsearchServicePreboot,
|
||||||
{ apiKey, hosts }: EnrollParameters
|
{ apiKey, hosts, caFingerprint }: EnrollParameters
|
||||||
): Promise<EnrollResult> {
|
): Promise<EnrollResult> {
|
||||||
const scopeableRequest: ScopeableRequest = { headers: { authorization: `ApiKey ${apiKey}` } };
|
const scopeableRequest: ScopeableRequest = { headers: { authorization: `ApiKey ${apiKey}` } };
|
||||||
const elasticsearchConfig: Partial<ElasticsearchClientConfig> = {
|
const elasticsearchConfig: Partial<ElasticsearchClientConfig> = {
|
||||||
|
@ -153,10 +153,14 @@ export class ElasticsearchService {
|
||||||
|
|
||||||
// We should iterate through all provided hosts until we find an accessible one.
|
// We should iterate through all provided hosts until we find an accessible one.
|
||||||
for (const host of hosts) {
|
for (const host of hosts) {
|
||||||
this.logger.debug(`Trying to enroll with "${host}" host`);
|
this.logger.debug(
|
||||||
|
`Trying to enroll with "${host}" host using "${caFingerprint}" CA fingerprint.`
|
||||||
|
);
|
||||||
|
|
||||||
const enrollClient = elasticsearch.createClient('enroll', {
|
const enrollClient = elasticsearch.createClient('enroll', {
|
||||||
...elasticsearchConfig,
|
...elasticsearchConfig,
|
||||||
hosts: [host],
|
hosts: [host],
|
||||||
|
caFingerprint,
|
||||||
});
|
});
|
||||||
|
|
||||||
let enrollmentResponse;
|
let enrollmentResponse;
|
||||||
|
@ -197,6 +201,7 @@ export class ElasticsearchService {
|
||||||
|
|
||||||
// Now try to use retrieved password and CA certificate to authenticate to this host.
|
// Now try to use retrieved password and CA certificate to authenticate to this host.
|
||||||
const authenticateClient = elasticsearch.createClient('authenticate', {
|
const authenticateClient = elasticsearch.createClient('authenticate', {
|
||||||
|
caFingerprint,
|
||||||
hosts: [host],
|
hosts: [host],
|
||||||
serviceAccountToken: enrollResult.serviceAccountToken.value,
|
serviceAccountToken: enrollResult.serviceAccountToken.value,
|
||||||
ssl: { certificateAuthorities: [enrollResult.ca] },
|
ssl: { certificateAuthorities: [enrollResult.ca] },
|
||||||
|
|
|
@ -113,7 +113,7 @@ describe('Enroll routes', () => {
|
||||||
mockRouteParams.preboot.isSetupOnHold.mockReturnValue(false);
|
mockRouteParams.preboot.isSetupOnHold.mockReturnValue(false);
|
||||||
|
|
||||||
const mockRequest = httpServerMock.createKibanaRequest({
|
const mockRequest = httpServerMock.createKibanaRequest({
|
||||||
body: { apiKey: 'some-key', hosts: ['host1', 'host2'], caFingerprint: 'ab:cd:ef' },
|
body: { apiKey: 'some-key', hosts: ['host1', 'host2'], caFingerprint: 'deadbeef' },
|
||||||
});
|
});
|
||||||
|
|
||||||
await expect(routeHandler(mockContext, mockRequest, kibanaResponseFactory)).resolves.toEqual({
|
await expect(routeHandler(mockContext, mockRequest, kibanaResponseFactory)).resolves.toEqual({
|
||||||
|
@ -134,7 +134,7 @@ describe('Enroll routes', () => {
|
||||||
);
|
);
|
||||||
|
|
||||||
const mockRequest = httpServerMock.createKibanaRequest({
|
const mockRequest = httpServerMock.createKibanaRequest({
|
||||||
body: { apiKey: 'some-key', hosts: ['host1', 'host2'], caFingerprint: 'ab:cd:ef' },
|
body: { apiKey: 'some-key', hosts: ['host1', 'host2'], caFingerprint: 'deadbeef' },
|
||||||
});
|
});
|
||||||
|
|
||||||
await expect(routeHandler(mockContext, mockRequest, kibanaResponseFactory)).resolves.toEqual({
|
await expect(routeHandler(mockContext, mockRequest, kibanaResponseFactory)).resolves.toEqual({
|
||||||
|
@ -164,7 +164,7 @@ describe('Enroll routes', () => {
|
||||||
mockRouteParams.kibanaConfigWriter.isConfigWritable.mockResolvedValue(false);
|
mockRouteParams.kibanaConfigWriter.isConfigWritable.mockResolvedValue(false);
|
||||||
|
|
||||||
const mockRequest = httpServerMock.createKibanaRequest({
|
const mockRequest = httpServerMock.createKibanaRequest({
|
||||||
body: { apiKey: 'some-key', hosts: ['host1', 'host2'], caFingerprint: 'ab:cd:ef' },
|
body: { apiKey: 'some-key', hosts: ['host1', 'host2'], caFingerprint: 'deadbeef' },
|
||||||
});
|
});
|
||||||
|
|
||||||
await expect(routeHandler(mockContext, mockRequest, kibanaResponseFactory)).resolves.toEqual({
|
await expect(routeHandler(mockContext, mockRequest, kibanaResponseFactory)).resolves.toEqual({
|
||||||
|
@ -203,7 +203,7 @@ describe('Enroll routes', () => {
|
||||||
);
|
);
|
||||||
|
|
||||||
const mockRequest = httpServerMock.createKibanaRequest({
|
const mockRequest = httpServerMock.createKibanaRequest({
|
||||||
body: { apiKey: 'some-key', hosts: ['host1', 'host2'], caFingerprint: 'ab:cd:ef' },
|
body: { apiKey: 'some-key', hosts: ['host1', 'host2'], caFingerprint: 'deadbeef' },
|
||||||
});
|
});
|
||||||
|
|
||||||
await expect(routeHandler(mockContext, mockRequest, kibanaResponseFactory)).resolves.toEqual({
|
await expect(routeHandler(mockContext, mockRequest, kibanaResponseFactory)).resolves.toEqual({
|
||||||
|
@ -236,7 +236,7 @@ describe('Enroll routes', () => {
|
||||||
);
|
);
|
||||||
|
|
||||||
const mockRequest = httpServerMock.createKibanaRequest({
|
const mockRequest = httpServerMock.createKibanaRequest({
|
||||||
body: { apiKey: 'some-key', hosts: ['host1', 'host2'], caFingerprint: 'ab:cd:ef' },
|
body: { apiKey: 'some-key', hosts: ['host1', 'host2'], caFingerprint: 'deadbeef' },
|
||||||
});
|
});
|
||||||
|
|
||||||
await expect(routeHandler(mockContext, mockRequest, kibanaResponseFactory)).resolves.toEqual({
|
await expect(routeHandler(mockContext, mockRequest, kibanaResponseFactory)).resolves.toEqual({
|
||||||
|
@ -273,7 +273,7 @@ describe('Enroll routes', () => {
|
||||||
mockRouteParams.kibanaConfigWriter.writeConfig.mockResolvedValue();
|
mockRouteParams.kibanaConfigWriter.writeConfig.mockResolvedValue();
|
||||||
|
|
||||||
const mockRequest = httpServerMock.createKibanaRequest({
|
const mockRequest = httpServerMock.createKibanaRequest({
|
||||||
body: { apiKey: 'some-key', hosts: ['host1', 'host2'], caFingerprint: 'ab:cd:ef' },
|
body: { apiKey: 'some-key', hosts: ['host1', 'host2'], caFingerprint: 'deadbeef' },
|
||||||
});
|
});
|
||||||
|
|
||||||
await expect(routeHandler(mockContext, mockRequest, kibanaResponseFactory)).resolves.toEqual({
|
await expect(routeHandler(mockContext, mockRequest, kibanaResponseFactory)).resolves.toEqual({
|
||||||
|
@ -286,7 +286,7 @@ describe('Enroll routes', () => {
|
||||||
expect(mockRouteParams.elasticsearch.enroll).toHaveBeenCalledWith({
|
expect(mockRouteParams.elasticsearch.enroll).toHaveBeenCalledWith({
|
||||||
apiKey: 'some-key',
|
apiKey: 'some-key',
|
||||||
hosts: ['host1', 'host2'],
|
hosts: ['host1', 'host2'],
|
||||||
caFingerprint: 'ab:cd:ef',
|
caFingerprint: 'DE:AD:BE:EF',
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(mockRouteParams.kibanaConfigWriter.writeConfig).toHaveBeenCalledTimes(1);
|
expect(mockRouteParams.kibanaConfigWriter.writeConfig).toHaveBeenCalledTimes(1);
|
||||||
|
|
|
@ -73,12 +73,20 @@ export function defineEnrollRoutes({
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Convert a plain hex string returned in the enrollment token to a format that ES client
|
||||||
|
// expects, i.e. to a colon delimited hex string in upper case: deadbeef -> DE:AD:BE:EF.
|
||||||
|
const colonFormattedCaFingerprint =
|
||||||
|
request.body.caFingerprint
|
||||||
|
.toUpperCase()
|
||||||
|
.match(/.{1,2}/g)
|
||||||
|
?.join(':') ?? '';
|
||||||
|
|
||||||
let enrollResult: EnrollResult;
|
let enrollResult: EnrollResult;
|
||||||
try {
|
try {
|
||||||
enrollResult = await elasticsearch.enroll({
|
enrollResult = await elasticsearch.enroll({
|
||||||
apiKey: request.body.apiKey,
|
apiKey: request.body.apiKey,
|
||||||
hosts: request.body.hosts,
|
hosts: request.body.hosts,
|
||||||
caFingerprint: request.body.caFingerprint,
|
caFingerprint: colonFormattedCaFingerprint,
|
||||||
});
|
});
|
||||||
} catch {
|
} catch {
|
||||||
// For security reasons, we shouldn't leak to the user whether Elasticsearch node couldn't process enrollment
|
// For security reasons, we shouldn't leak to the user whether Elasticsearch node couldn't process enrollment
|
||||||
|
|
Loading…
Reference in a new issue