mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -04:00
[Fleet] Download Elastic GPG key during build (#134861)
* add build step to download gpg key * add gpg path to config * add getGpgKey method * getGpgKey reads config directly * improve logging * return undefined on error * log error code instead of msg * perform checksum check on GPG key * fail build if GPG key download fails
This commit is contained in:
parent
31db97850d
commit
69caa311bb
7 changed files with 151 additions and 2 deletions
|
@ -88,6 +88,7 @@ export async function buildDistributables(log: ToolingLog, options: BuildOptions
|
|||
await run(Tasks.CleanTypescript);
|
||||
await run(Tasks.CleanExtraFilesFromModules);
|
||||
await run(Tasks.CleanEmptyFolders);
|
||||
await run(Tasks.FleetDownloadElasticGpgKey);
|
||||
await run(Tasks.BundleFleetPackages);
|
||||
}
|
||||
|
||||
|
|
40
src/dev/build/tasks/fleet_download_elastic_gpg_key.ts
Normal file
40
src/dev/build/tasks/fleet_download_elastic_gpg_key.ts
Normal file
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import { Task, downloadToDisk } from '../lib';
|
||||
|
||||
const BUNDLED_KEYS_DIR = 'x-pack/plugins/fleet/target/keys';
|
||||
const ARTIFACTS_URL = 'https://artifacts.elastic.co/';
|
||||
const GPG_KEY_NAME = 'GPG-KEY-elasticsearch';
|
||||
const GPG_KEY_SHA512 =
|
||||
'84ee193cc337344d9a7da9021daf3f5ede83f5f1ab049d169f3634921529dcd096abf7a91eec7f26f3a6913e5e38f88f69a5e2ce79ad155d46edc75705a648c6';
|
||||
|
||||
export const FleetDownloadElasticGpgKey: Task = {
|
||||
description: 'Downloading Elastic GPG key for Fleet',
|
||||
|
||||
async run(config, log, build) {
|
||||
const gpgKeyUrl = ARTIFACTS_URL + GPG_KEY_NAME;
|
||||
const destination = build.resolvePath(BUNDLED_KEYS_DIR, GPG_KEY_NAME);
|
||||
log.info(`Downloading Elastic GPG key from ${gpgKeyUrl} to ${destination}`);
|
||||
|
||||
try {
|
||||
await downloadToDisk({
|
||||
log,
|
||||
url: gpgKeyUrl,
|
||||
destination,
|
||||
shaChecksum: GPG_KEY_SHA512,
|
||||
shaAlgorithm: 'sha512',
|
||||
skipChecksumCheck: false,
|
||||
maxAttempts: 3,
|
||||
});
|
||||
} catch (error) {
|
||||
log.error(`Error downloading Elastic GPG key from ${gpgKeyUrl} to ${destination}`);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
};
|
|
@ -7,8 +7,8 @@
|
|||
*/
|
||||
|
||||
export * from './bin';
|
||||
export * from './build_kibana_platform_plugins';
|
||||
export * from './build_kibana_example_plugins';
|
||||
export * from './build_kibana_platform_plugins';
|
||||
export * from './build_packages_task';
|
||||
export * from './bundle_fleet_packages';
|
||||
export * from './clean_tasks';
|
||||
|
@ -18,6 +18,7 @@ export * from './create_archives_task';
|
|||
export * from './create_empty_dirs_and_files_task';
|
||||
export * from './create_readme_task';
|
||||
export * from './download_cloud_dependencies';
|
||||
export * from './fleet_download_elastic_gpg_key';
|
||||
export * from './generate_packages_optimized_assets';
|
||||
export * from './install_dependencies_task';
|
||||
export * from './license_file_task';
|
||||
|
@ -27,11 +28,11 @@ export * from './os_packages';
|
|||
export * from './package_json';
|
||||
export * from './patch_native_modules_task';
|
||||
export * from './path_length_task';
|
||||
export * from './replace_favicon';
|
||||
export * from './transpile_babel_task';
|
||||
export * from './uuid_verification_task';
|
||||
export * from './verify_env_task';
|
||||
export * from './write_sha_sums_task';
|
||||
export * from './replace_favicon';
|
||||
|
||||
// @ts-expect-error this module can't be TS because it ends up pulling x-pack into Kibana
|
||||
export { InstallChromium } from './install_chromium';
|
||||
|
|
|
@ -33,6 +33,9 @@ export interface FleetConfigType {
|
|||
outputs?: PreconfiguredOutput[];
|
||||
agentIdVerificationEnabled?: boolean;
|
||||
enableExperimental?: string[];
|
||||
packageVerification?: {
|
||||
gpgKeyPath?: string;
|
||||
};
|
||||
developer?: {
|
||||
disableRegistryVersionCheck?: boolean;
|
||||
allowAgentUpgradeSourceUri?: boolean;
|
||||
|
|
|
@ -47,6 +47,7 @@ export type {
|
|||
export { AgentNotFoundError, FleetUnauthorizedError } from './errors';
|
||||
|
||||
const DEFAULT_BUNDLED_PACKAGE_LOCATION = path.join(__dirname, '../target/bundled_packages');
|
||||
const DEFAULT_GPG_KEY_PATH = path.join(__dirname, '../target/keys/GPG-KEY-elasticsearch');
|
||||
|
||||
export const config: PluginConfigDescriptor = {
|
||||
exposeToBrowser: {
|
||||
|
@ -143,6 +144,9 @@ export const config: PluginConfigDescriptor = {
|
|||
allowAgentUpgradeSourceUri: schema.boolean({ defaultValue: false }),
|
||||
bundledPackageLocation: schema.string({ defaultValue: DEFAULT_BUNDLED_PACKAGE_LOCATION }),
|
||||
}),
|
||||
packageVerification: schema.object({
|
||||
gpgKeyPath: schema.string({ defaultValue: DEFAULT_GPG_KEY_PATH }),
|
||||
}),
|
||||
/**
|
||||
* For internal use. A list of string values (comma delimited) that will enable experimental
|
||||
* type of functionality that is not yet released.
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* 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; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { readFile } from 'fs/promises';
|
||||
|
||||
import { loggingSystemMock } from '@kbn/core-logging-server-mocks';
|
||||
|
||||
const mockLoggerFactory = loggingSystemMock.create();
|
||||
const mockLogger = mockLoggerFactory.get('mock logger');
|
||||
import { getGpgKeyOrUndefined, _readGpgKey } from './package_verification';
|
||||
|
||||
const mockGetConfig = jest.fn();
|
||||
jest.mock('../../app_context', () => ({
|
||||
appContextService: {
|
||||
getConfig: () => mockGetConfig(),
|
||||
getLogger: () => mockLogger,
|
||||
},
|
||||
}));
|
||||
|
||||
jest.mock('fs/promises', () => ({
|
||||
readFile: jest.fn(),
|
||||
}));
|
||||
|
||||
const mockedReadFile = readFile as jest.MockedFunction<typeof readFile>;
|
||||
|
||||
beforeEach(() => {
|
||||
jest.resetAllMocks();
|
||||
});
|
||||
describe('getGpgKeyOrUndefined', () => {
|
||||
it('should cache the gpg key after reading file once', async () => {
|
||||
const keyContent = 'this is the gpg key';
|
||||
mockedReadFile.mockResolvedValue(Buffer.from(keyContent));
|
||||
mockGetConfig.mockReturnValue({ packageVerification: { gpgKeyPath: 'somePath' } });
|
||||
expect(await getGpgKeyOrUndefined()).toEqual(keyContent);
|
||||
expect(await getGpgKeyOrUndefined()).toEqual(keyContent);
|
||||
expect(mockedReadFile).toHaveBeenCalledWith('somePath');
|
||||
expect(mockedReadFile).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('_readGpgKey', () => {
|
||||
it('should return undefined if the key file isnt configured', async () => {
|
||||
mockedReadFile.mockResolvedValue(Buffer.from('this is the gpg key'));
|
||||
mockGetConfig.mockReturnValue({ packageVerification: {} });
|
||||
|
||||
expect(await _readGpgKey()).toEqual(undefined);
|
||||
});
|
||||
it('should return undefined if there is an error reading the file', async () => {
|
||||
mockedReadFile.mockRejectedValue(new Error('some error'));
|
||||
mockGetConfig.mockReturnValue({ packageVerification: { gpgKeyPath: 'somePath' } });
|
||||
expect(await _readGpgKey()).toEqual(undefined);
|
||||
expect(mockedReadFile).toHaveBeenCalledWith('somePath');
|
||||
});
|
||||
});
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* 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; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { readFile } from 'fs/promises';
|
||||
|
||||
import { appContextService } from '../../app_context';
|
||||
|
||||
let cachedKey: string | undefined | null = null;
|
||||
|
||||
export async function getGpgKeyOrUndefined(): Promise<string | undefined> {
|
||||
if (cachedKey !== null) return cachedKey;
|
||||
|
||||
cachedKey = await _readGpgKey();
|
||||
return cachedKey;
|
||||
}
|
||||
|
||||
export async function _readGpgKey(): Promise<string | undefined> {
|
||||
const config = appContextService.getConfig();
|
||||
const logger = appContextService.getLogger();
|
||||
const gpgKeyPath = config?.packageVerification?.gpgKeyPath;
|
||||
|
||||
if (!gpgKeyPath) {
|
||||
logger.warn('GPG key path not configured at "xpack.fleet.packageVerification.gpgKeyPath"');
|
||||
return undefined;
|
||||
}
|
||||
|
||||
let buffer: Buffer;
|
||||
try {
|
||||
buffer = await readFile(gpgKeyPath);
|
||||
} catch (e) {
|
||||
logger.warn(`Unable to retrieve GPG key from '${gpgKeyPath}': ${e.code}`);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const key = buffer.toString();
|
||||
cachedKey = key;
|
||||
return key;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue