mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 09:19:04 -04:00
[Security Solution][Endpoint] Allow apiKey
to be used when creating KBN/ES clients for use in scripts (#166187)
## Summary - Adds support for `apiKey` to the CLI service methods: `createKbnClient()`, `createEsClient()` and `createRuntimeServices()` - Note: no existing CLI tools have been changed with this PR to support `apiKey`. Only adding support to the above service methods so that they can be used by existing or new CLI utilities
This commit is contained in:
parent
6fc5c806ed
commit
5a3a3c8039
3 changed files with 73 additions and 18 deletions
3
.github/CODEOWNERS
vendored
3
.github/CODEOWNERS
vendored
|
@ -1304,8 +1304,7 @@ x-pack/plugins/cloud_integrations/cloud_full_story/server/config.ts @elastic/kib
|
|||
/x-pack/plugins/security_solution/server/lists_integration/endpoint/ @elastic/security-defend-workflows
|
||||
/x-pack/plugins/security_solution/server/lib/license/ @elastic/security-defend-workflows
|
||||
/x-pack/plugins/security_solution/server/fleet_integration/ @elastic/security-defend-workflows
|
||||
/x-pack/plugins/security_solution/scripts/endpoint/event_filters/ @elastic/security-defend-workflows
|
||||
/x-pack/plugins/security_solution/scripts/endpoint/trusted_apps/ @elastic/security-defend-workflows
|
||||
/x-pack/plugins/security_solution/scripts/endpoint/ @elastic/security-defend-workflows
|
||||
/x-pack/test/security_solution_endpoint/ @elastic/security-defend-workflows
|
||||
/x-pack/test/security_solution_endpoint_api_int/ @elastic/security-defend-workflows
|
||||
/x-pack/test_serverless/shared/lib/security/kibana_roles/ @elastic/security-defend-workflows
|
||||
|
|
|
@ -37,6 +37,7 @@ import type {
|
|||
import nodeFetch from 'node-fetch';
|
||||
import semver from 'semver';
|
||||
import axios from 'axios';
|
||||
import { fetchKibanaStatus } from './stack_services';
|
||||
import { catchAxiosErrorFormatAndThrow } from './format_axios_error';
|
||||
import { FleetAgentGenerator } from '../../../common/endpoint/data_generators/fleet_agent_generator';
|
||||
|
||||
|
@ -249,7 +250,7 @@ export const fetchAgentPolicyList = async (
|
|||
export const getAgentVersionMatchingCurrentStack = async (
|
||||
kbnClient: KbnClient
|
||||
): Promise<string> => {
|
||||
const kbnStatus = await kbnClient.status.get();
|
||||
const kbnStatus = await fetchKibanaStatus(kbnClient);
|
||||
const agentVersions = await axios
|
||||
.get('https://artifacts-api.elastic.co/v1/versions')
|
||||
.then((response) => map(response.data.versions, (version) => version.split('-SNAPSHOT')[0]));
|
||||
|
|
|
@ -7,10 +7,14 @@
|
|||
|
||||
import { Client } from '@elastic/elasticsearch';
|
||||
import { ToolingLog } from '@kbn/tooling-log';
|
||||
import type { KbnClientOptions } from '@kbn/test';
|
||||
import { KbnClient } from '@kbn/test';
|
||||
import type { StatusResponse } from '@kbn/core-status-common-internal';
|
||||
import pRetry from 'p-retry';
|
||||
import nodeFetch from 'node-fetch';
|
||||
import type { ReqOptions } from '@kbn/test/src/kbn_client/kbn_client_requester';
|
||||
import { type AxiosResponse } from 'axios';
|
||||
import type { ClientOptions } from '@elastic/elasticsearch/lib/client';
|
||||
import { catchAxiosErrorFormatAndThrow } from './format_axios_error';
|
||||
import { isLocalhost } from './is_localhost';
|
||||
import { getLocalhostRealIp } from './localhost_services';
|
||||
|
@ -24,6 +28,7 @@ export interface RuntimeServices {
|
|||
username: string;
|
||||
password: string;
|
||||
}>;
|
||||
apiKey: string;
|
||||
localhostRealIp: string;
|
||||
kibana: {
|
||||
url: string;
|
||||
|
@ -51,6 +56,8 @@ interface CreateRuntimeServicesOptions {
|
|||
fleetServerUrl?: string;
|
||||
username: string;
|
||||
password: string;
|
||||
/** If defined, both `username` and `password` will be ignored */
|
||||
apiKey?: string;
|
||||
/** If undefined, ES username defaults to `username` */
|
||||
esUsername?: string;
|
||||
/** If undefined, ES password defaults to `password` */
|
||||
|
@ -59,12 +66,41 @@ interface CreateRuntimeServicesOptions {
|
|||
asSuperuser?: boolean;
|
||||
}
|
||||
|
||||
class KbnClientExtended extends KbnClient {
|
||||
private readonly apiKey: string | undefined;
|
||||
|
||||
constructor({ apiKey, url, ...options }: KbnClientOptions & { apiKey?: string }) {
|
||||
super({
|
||||
...options,
|
||||
url: apiKey ? buildUrlWithCredentials(url, '', '') : url,
|
||||
});
|
||||
|
||||
this.apiKey = apiKey;
|
||||
}
|
||||
|
||||
async request<T>(options: ReqOptions): Promise<AxiosResponse<T>> {
|
||||
const headers: ReqOptions['headers'] = {
|
||||
...(options.headers ?? {}),
|
||||
};
|
||||
|
||||
if (this.apiKey) {
|
||||
headers.Authorization = `ApiKey ${this.apiKey}`;
|
||||
}
|
||||
|
||||
return super.request({
|
||||
...options,
|
||||
headers,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export const createRuntimeServices = async ({
|
||||
kibanaUrl,
|
||||
elasticsearchUrl,
|
||||
fleetServerUrl = 'https://localhost:8220',
|
||||
username: _username,
|
||||
password: _password,
|
||||
apiKey,
|
||||
esUsername,
|
||||
esPassword,
|
||||
log = new ToolingLog({ level: 'info', writeTo: process.stdout }),
|
||||
|
@ -97,15 +133,17 @@ export const createRuntimeServices = async ({
|
|||
const fleetURL = new URL(fleetServerUrl);
|
||||
|
||||
return {
|
||||
kbnClient: createKbnClient({ log, url: kibanaUrl, username, password }),
|
||||
kbnClient: createKbnClient({ log, url: kibanaUrl, username, password, apiKey }),
|
||||
esClient: createEsClient({
|
||||
log,
|
||||
url: elasticsearchUrl,
|
||||
username: esUsername ?? username,
|
||||
password: esPassword ?? password,
|
||||
apiKey,
|
||||
}),
|
||||
log,
|
||||
localhostRealIp: await getLocalhostRealIp(),
|
||||
apiKey: apiKey ?? '',
|
||||
user: {
|
||||
username,
|
||||
password,
|
||||
|
@ -148,40 +186,54 @@ export const createEsClient = ({
|
|||
url,
|
||||
username,
|
||||
password,
|
||||
apiKey,
|
||||
log,
|
||||
}: {
|
||||
url: string;
|
||||
username: string;
|
||||
password: string;
|
||||
/** If defined, both `username` and `password` will be ignored */
|
||||
apiKey?: string;
|
||||
log?: ToolingLog;
|
||||
}): Client => {
|
||||
const esUrl = buildUrlWithCredentials(url, username, password);
|
||||
const clientOptions: ClientOptions = {
|
||||
node: buildUrlWithCredentials(url, apiKey ? '' : username, apiKey ? '' : password),
|
||||
};
|
||||
|
||||
if (log) {
|
||||
log.verbose(`Creating Elasticsearch client with URL: ${esUrl}`);
|
||||
if (apiKey) {
|
||||
clientOptions.auth = { apiKey };
|
||||
}
|
||||
|
||||
return new Client({ node: esUrl });
|
||||
if (log) {
|
||||
log.verbose(`Creating Elasticsearch client options: ${JSON.stringify(clientOptions)}`);
|
||||
}
|
||||
|
||||
return new Client(clientOptions);
|
||||
};
|
||||
|
||||
export const createKbnClient = ({
|
||||
url,
|
||||
username,
|
||||
password,
|
||||
apiKey,
|
||||
log = new ToolingLog(),
|
||||
}: {
|
||||
url: string;
|
||||
username: string;
|
||||
password: string;
|
||||
/** If defined, both `username` and `password` will be ignored */
|
||||
apiKey?: string;
|
||||
log?: ToolingLog;
|
||||
}): KbnClient => {
|
||||
const kbnUrl = buildUrlWithCredentials(url, username, password);
|
||||
|
||||
if (log) {
|
||||
log.verbose(`Creating Kibana client with URL: ${kbnUrl}`);
|
||||
log.verbose(
|
||||
`Creating Kibana client with URL: ${kbnUrl} ${apiKey ? ` + ApiKey: ${apiKey}` : ''}`
|
||||
);
|
||||
}
|
||||
|
||||
return new KbnClient({ log, url: kbnUrl });
|
||||
return new KbnClientExtended({ log, url: kbnUrl, apiKey });
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -189,14 +241,7 @@ export const createKbnClient = ({
|
|||
* @param kbnClient
|
||||
*/
|
||||
export const fetchStackVersion = async (kbnClient: KbnClient): Promise<string> => {
|
||||
const status = (
|
||||
await kbnClient
|
||||
.request<StatusResponse>({
|
||||
method: 'GET',
|
||||
path: '/api/status',
|
||||
})
|
||||
.catch(catchAxiosErrorFormatAndThrow)
|
||||
).data;
|
||||
const status = await fetchKibanaStatus(kbnClient);
|
||||
|
||||
if (!status?.version?.number) {
|
||||
throw new Error(
|
||||
|
@ -207,6 +252,16 @@ export const fetchStackVersion = async (kbnClient: KbnClient): Promise<string> =
|
|||
return status.version.number;
|
||||
};
|
||||
|
||||
export const fetchKibanaStatus = async (kbnClient: KbnClient): Promise<StatusResponse> => {
|
||||
return kbnClient
|
||||
.request<StatusResponse>({
|
||||
method: 'GET',
|
||||
path: '/api/status',
|
||||
})
|
||||
.catch(catchAxiosErrorFormatAndThrow)
|
||||
.then((response) => response.data);
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks to ensure Kibana is up and running
|
||||
* @param kbnUrl
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue