From 87e97aaedac574ba4dc343233049e03a0970bf1e Mon Sep 17 00:00:00 2001 From: Mark Hopkin Date: Tue, 28 Jun 2022 15:31:42 +0100 Subject: [PATCH 01/18] add verifyPackageArchiveSignature fn --- .../plugins/fleet/common/types/models/epm.ts | 1 + .../epm/packages/package_verification.ts | 68 ++++++++++++++++--- .../server/services/epm/registry/index.ts | 29 +++++++- 3 files changed, 86 insertions(+), 12 deletions(-) diff --git a/x-pack/plugins/fleet/common/types/models/epm.ts b/x-pack/plugins/fleet/common/types/models/epm.ts index d33616e369abe..dc8f4b9dfe40c 100644 --- a/x-pack/plugins/fleet/common/types/models/epm.ts +++ b/x-pack/plugins/fleet/common/types/models/epm.ts @@ -134,6 +134,7 @@ type RegistryOverridesToOptional = Pick { @@ -46,28 +56,64 @@ export async function _readGpgKey(): Promise { return key; } -export interface PackageVerificationResult { - isVerified: boolean; - keyId: string; +export async function verifyPackageArchiveSignature({ + pkgName, + pkgVersion, + logger, +}: { + pkgName: string; + pkgVersion: string; + logger: Logger; +}): Promise { + const verificationKey = await getGpgKeyOrUndefined(); + + if (!verificationKey) { + logger.warn(`Not performing package verification as no local verification key found`); + return { didVerify: false }; + } + const pkgArchiveSignature = await Registry.getPackageArchiveSignatureOrUndefined({ + pkgName, + pkgVersion, + logger, + }); + + if (!pkgArchiveSignature) { + logger.warn(`Not performing package verification as package has no signature file`); + return { didVerify: false }; + } + + const { archiveBuffer: pkgArchiveBuffer } = await Registry.fetchArchiveBuffer( + pkgName, + pkgVersion + ); + + const verificationResult = await _verifyPackageSignature({ + pkgArchiveBuffer, + pkgArchiveSignature, + verificationKey, + logger, + }); + + return { didVerify: true, ...verificationResult }; } -export async function verifyPackageSignature({ - pkgZipBuffer, - pkgZipSignature, +async function _verifyPackageSignature({ + pkgArchiveBuffer, + pkgArchiveSignature, verificationKey, logger, }: { - pkgZipBuffer: Buffer; - pkgZipSignature: string; + pkgArchiveBuffer: Buffer; + pkgArchiveSignature: string; verificationKey: openpgp.Key; logger: Logger; -}): Promise { +}): Promise { const signature = await openpgp.readSignature({ - armoredSignature: pkgZipSignature, + armoredSignature: pkgArchiveSignature, }); const message = await openpgp.createMessage({ - binary: pkgZipBuffer, + binary: pkgArchiveBuffer, }); const verificationResult = await openpgp.verify({ diff --git a/x-pack/plugins/fleet/server/services/epm/registry/index.ts b/x-pack/plugins/fleet/server/services/epm/registry/index.ts index 1074e975d3f6f..e690efce1c69f 100644 --- a/x-pack/plugins/fleet/server/services/epm/registry/index.ts +++ b/x-pack/plugins/fleet/server/services/epm/registry/index.ts @@ -11,6 +11,7 @@ import mime from 'mime-types'; import semverGte from 'semver/functions/gte'; import type { Response } from 'node-fetch'; +import type { Logger } from '@kbn/logging'; import { splitPkgKey as split } from '../../../../common'; @@ -31,7 +32,7 @@ import { getPackageInfo, setPackageInfo, } from '../archive'; -import { streamToBuffer } from '../streams'; +import { streamToBuffer, streamToString } from '../streams'; import { appContextService } from '../..'; import { PackageNotFoundError, PackageCacheError, RegistryResponseError } from '../../../errors'; @@ -284,6 +285,32 @@ export async function fetchArchiveBuffer( return { archiveBuffer, archivePath }; } +export async function getPackageArchiveSignatureOrUndefined({ + pkgName, + pkgVersion, + logger, +}: { + pkgName: string; + pkgVersion: string; + logger: Logger; +}): Promise { + const { signature_path: signaturePath } = await getInfo(pkgName, pkgVersion); + + if (!signaturePath) { + logger.warn(`Package ${pkgName}-${pkgVersion} does not have signature_file specified.`); + return undefined; + } + + try { + const { body } = await fetchFile(signaturePath); + + return streamToString(body); + } catch (e) { + logger.error(`Error retrieving package signature at '${signaturePath}' : ${e}`); + return undefined; + } +} + export function groupPathsByService(paths: string[]): AssetsGroupedByServiceByType { const kibanaAssetTypes = Object.values(KibanaAssetType); From a604e6faadc5c86355278861c2f442d47ac848f7 Mon Sep 17 00:00:00 2001 From: Mark Hopkin Date: Tue, 28 Jun 2022 15:50:45 +0100 Subject: [PATCH 02/18] add feature flag --- x-pack/plugins/fleet/common/experimental_features.ts | 1 + .../fleet/server/services/epm/packages/install.ts | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/x-pack/plugins/fleet/common/experimental_features.ts b/x-pack/plugins/fleet/common/experimental_features.ts index 9fd95d6843e12..9a49e403671e2 100644 --- a/x-pack/plugins/fleet/common/experimental_features.ts +++ b/x-pack/plugins/fleet/common/experimental_features.ts @@ -13,6 +13,7 @@ export type ExperimentalFeatures = typeof allowedExperimentalValues; */ export const allowedExperimentalValues = Object.freeze({ createPackagePolicyMultiPageLayout: true, + packageVerification: false, }); type ExperimentalConfigKeys = Array; diff --git a/x-pack/plugins/fleet/server/services/epm/packages/install.ts b/x-pack/plugins/fleet/server/services/epm/packages/install.ts index 90a41add5f184..560a2e502c141 100644 --- a/x-pack/plugins/fleet/server/services/epm/packages/install.ts +++ b/x-pack/plugins/fleet/server/services/epm/packages/install.ts @@ -47,6 +47,8 @@ import type { ArchiveAsset } from '../kibana/assets/install'; import type { PackageUpdateEvent } from '../../upgrade_sender'; import { sendTelemetryEvents, UpdateEventType } from '../../upgrade_sender'; +import { verifyPackageArchiveSignature } from './package_verification'; + import { getInstallation, getInstallationObject } from '.'; import { removeInstallation } from './remove'; import { getPackageSavedObjects } from './get'; @@ -340,6 +342,13 @@ async function installPackageFromRegistry({ .getSavedObjects() .createImporter(savedObjectsClient); + if (appContextService.getExperimentalFeatures().packageVerification) { + const verificationResult = await verifyPackageArchiveSignature({ + pkgName, + pkgVersion, + logger, + }); + } // try installing the package, if there was an error, call error handler and rethrow // @ts-expect-error status is string instead of InstallResult.status 'installed' | 'already_installed' return await _installPackage({ From 0f856b66742b1fee3a552b807914ce018b1ab1a1 Mon Sep 17 00:00:00 2001 From: Mark Hopkin Date: Tue, 28 Jun 2022 17:23:17 +0100 Subject: [PATCH 03/18] throw error if package verification fails --- x-pack/plugins/fleet/server/errors/index.ts | 5 +++++ .../fleet/server/services/epm/packages/install.ts | 10 +++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/fleet/server/errors/index.ts b/x-pack/plugins/fleet/server/errors/index.ts index 20f077d6e4e4d..cbfcf5733f736 100644 --- a/x-pack/plugins/fleet/server/errors/index.ts +++ b/x-pack/plugins/fleet/server/errors/index.ts @@ -31,6 +31,11 @@ export class RegistryResponseError extends RegistryError { export class PackageNotFoundError extends IngestManagerError {} export class PackageKeyInvalidError extends IngestManagerError {} export class PackageOutdatedError extends IngestManagerError {} +export class PackageFailedVerificationError extends IngestManagerError { + constructor(pkgKey: string) { + super(`${pkgKey} failed signature verification.`); + } +} export class AgentPolicyError extends IngestManagerError {} export class AgentPolicyNotFoundError extends IngestManagerError {} export class AgentNotFoundError extends IngestManagerError {} diff --git a/x-pack/plugins/fleet/server/services/epm/packages/install.ts b/x-pack/plugins/fleet/server/services/epm/packages/install.ts index 560a2e502c141..6cc94a34c00ff 100644 --- a/x-pack/plugins/fleet/server/services/epm/packages/install.ts +++ b/x-pack/plugins/fleet/server/services/epm/packages/install.ts @@ -29,7 +29,11 @@ import type { InstallSource, } from '../../../../common'; import { AUTO_UPGRADE_POLICIES_PACKAGES } from '../../../../common'; -import { IngestManagerError, PackageOutdatedError } from '../../../errors'; +import { + IngestManagerError, + PackageOutdatedError, + PackageFailedVerificationError, +} from '../../../errors'; import { PACKAGES_SAVED_OBJECT_TYPE, MAX_TIME_COMPLETE_INSTALL } from '../../../constants'; import type { KibanaAssetType } from '../../../types'; import { licenseService } from '../..'; @@ -348,6 +352,10 @@ async function installPackageFromRegistry({ pkgVersion, logger, }); + const isUnverified = verificationResult.didVerify && !verificationResult.isVerified; + if (isUnverified && !force) { + throw new PackageFailedVerificationError(pkgkey); + } } // try installing the package, if there was an error, call error handler and rethrow // @ts-expect-error status is string instead of InstallResult.status 'installed' | 'already_installed' From 03bd31e1567fc9ea78a1d1f55c55e2d8b63b47d1 Mon Sep 17 00:00:00 2001 From: Mark Hopkin Date: Tue, 28 Jun 2022 21:17:09 +0100 Subject: [PATCH 04/18] add verification info to saved object --- .../plugins/fleet/common/types/models/epm.ts | 5 ++ .../fleet/server/saved_objects/index.ts | 8 +++ .../services/epm/packages/_install_package.ts | 4 ++ .../server/services/epm/packages/install.ts | 56 +++++++++++++------ .../epm/packages/package_verification.ts | 10 ++-- 5 files changed, 61 insertions(+), 22 deletions(-) diff --git a/x-pack/plugins/fleet/common/types/models/epm.ts b/x-pack/plugins/fleet/common/types/models/epm.ts index dc8f4b9dfe40c..a56027a570251 100644 --- a/x-pack/plugins/fleet/common/types/models/epm.ts +++ b/x-pack/plugins/fleet/common/types/models/epm.ts @@ -420,6 +420,11 @@ export interface Installation extends SavedObjectAttributes { installed_kibana_space_id?: string; keep_policies_up_to_date?: boolean; install_format_schema_version?: string; + verification?: { + verification_attempted?: boolean; + is_verified?: boolean; + key_id?: string; + }; } export interface PackageUsageStats { diff --git a/x-pack/plugins/fleet/server/saved_objects/index.ts b/x-pack/plugins/fleet/server/saved_objects/index.ts index fb64803a7e6d5..0b64402391453 100644 --- a/x-pack/plugins/fleet/server/saved_objects/index.ts +++ b/x-pack/plugins/fleet/server/saved_objects/index.ts @@ -231,6 +231,14 @@ const getSavedObjectTypes = ( enabled: false, type: 'object', }, + verification: { + type: 'object', + properties: { + verification_attempted: { type: 'boolean' }, + is_verified: { type: 'boolean' }, + key_id: { type: 'keyword' }, + }, + }, installed_es: { type: 'nested', properties: { diff --git a/x-pack/plugins/fleet/server/services/epm/packages/_install_package.ts b/x-pack/plugins/fleet/server/services/epm/packages/_install_package.ts index 57e293257d176..ffc36b87cb41e 100644 --- a/x-pack/plugins/fleet/server/services/epm/packages/_install_package.ts +++ b/x-pack/plugins/fleet/server/services/epm/packages/_install_package.ts @@ -41,6 +41,7 @@ import { packagePolicyService } from '../..'; import { createInstallation, updateEsAssetReferences } from './install'; import { withPackageSpan } from './utils'; +import type { PackageVerificationResult } from './package_verification'; // this is only exported for testing // use a leading underscore to indicate it's not the supported path @@ -56,6 +57,7 @@ export async function _installPackage({ installType, installSource, spaceId, + verificationResult, }: { savedObjectsClient: SavedObjectsClientContract; savedObjectsImporter: Pick; @@ -67,6 +69,7 @@ export async function _installPackage({ installType: InstallType; installSource: InstallSource; spaceId: string; + verificationResult?: PackageVerificationResult; }): Promise { const { name: pkgName, version: pkgVersion } = packageInfo; @@ -101,6 +104,7 @@ export async function _installPackage({ packageInfo, installSource, spaceId, + verificationResult, }); } diff --git a/x-pack/plugins/fleet/server/services/epm/packages/install.ts b/x-pack/plugins/fleet/server/services/epm/packages/install.ts index 6cc94a34c00ff..ee2614f94975d 100644 --- a/x-pack/plugins/fleet/server/services/epm/packages/install.ts +++ b/x-pack/plugins/fleet/server/services/epm/packages/install.ts @@ -51,6 +51,7 @@ import type { ArchiveAsset } from '../kibana/assets/install'; import type { PackageUpdateEvent } from '../../upgrade_sender'; import { sendTelemetryEvents, UpdateEventType } from '../../upgrade_sender'; +import type { PackageVerificationResult } from './package_verification'; import { verifyPackageArchiveSignature } from './package_verification'; import { getInstallation, getInstallationObject } from '.'; @@ -346,13 +347,15 @@ async function installPackageFromRegistry({ .getSavedObjects() .createImporter(savedObjectsClient); + let verificationResult; if (appContextService.getExperimentalFeatures().packageVerification) { - const verificationResult = await verifyPackageArchiveSignature({ + verificationResult = await verifyPackageArchiveSignature({ pkgName, pkgVersion, logger, }); - const isUnverified = verificationResult.didVerify && !verificationResult.isVerified; + const isUnverified = + verificationResult.verificationAttempted && !verificationResult.isVerified; if (isUnverified && !force) { throw new PackageFailedVerificationError(pkgkey); } @@ -369,6 +372,7 @@ async function installPackageFromRegistry({ packageInfo, installType, spaceId, + verificationResult, installSource: 'registry', }) .then(async (assets) => { @@ -602,8 +606,9 @@ export async function createInstallation(options: { packageInfo: InstallablePackage; installSource: InstallSource; spaceId: string; + verificationResult?: PackageVerificationResult; }) { - const { savedObjectsClient, packageInfo, installSource } = options; + const { savedObjectsClient, packageInfo, installSource, verificationResult } = options; const { name: pkgName, version: pkgVersion } = packageInfo; const toSaveESIndexPatterns = generateESIndexPatterns(packageInfo.data_streams); @@ -616,23 +621,38 @@ export async function createInstallation(options: { ? true : undefined; + const savedObject: Installation = { + installed_kibana: [], + installed_kibana_space_id: options.spaceId, + installed_es: [], + package_assets: [], + es_index_patterns: toSaveESIndexPatterns, + name: pkgName, + version: pkgVersion, + install_version: pkgVersion, + install_status: 'installing', + install_started_at: new Date().toISOString(), + install_source: installSource, + install_format_schema_version: FLEET_INSTALL_FORMAT_VERSION, + keep_policies_up_to_date: defaultKeepPoliciesUpToDate, + }; + + if (verificationResult) { + const verification: Installation['verification'] = { + verification_attempted: verificationResult.verificationAttempted, + }; + + if (verificationResult.verificationAttempted) { + verification.is_verified = verificationResult.isVerified; + verification.key_id = verificationResult.keyId; + } + + savedObject.verification = verification; + } + const created = await savedObjectsClient.create( PACKAGES_SAVED_OBJECT_TYPE, - { - installed_kibana: [], - installed_kibana_space_id: options.spaceId, - installed_es: [], - package_assets: [], - es_index_patterns: toSaveESIndexPatterns, - name: pkgName, - version: pkgVersion, - install_version: pkgVersion, - install_status: 'installing', - install_started_at: new Date().toISOString(), - install_source: installSource, - install_format_schema_version: FLEET_INSTALL_FORMAT_VERSION, - keep_policies_up_to_date: defaultKeepPoliciesUpToDate, - }, + savedObject, { id: pkgName, overwrite: true } ); diff --git a/x-pack/plugins/fleet/server/services/epm/packages/package_verification.ts b/x-pack/plugins/fleet/server/services/epm/packages/package_verification.ts index 6a210b7500761..8234da5812783 100644 --- a/x-pack/plugins/fleet/server/services/epm/packages/package_verification.ts +++ b/x-pack/plugins/fleet/server/services/epm/packages/package_verification.ts @@ -19,7 +19,9 @@ interface VerificationResult { keyId: string; } -type PackageVerificationResult = { didVerify: false } | ({ didVerify: true } & VerificationResult); +export type PackageVerificationResult = + | { verificationAttempted: false } + | ({ verificationAttempted: true } & VerificationResult); let cachedKey: openpgp.Key | undefined | null = null; @@ -69,7 +71,7 @@ export async function verifyPackageArchiveSignature({ if (!verificationKey) { logger.warn(`Not performing package verification as no local verification key found`); - return { didVerify: false }; + return { verificationAttempted: false }; } const pkgArchiveSignature = await Registry.getPackageArchiveSignatureOrUndefined({ pkgName, @@ -79,7 +81,7 @@ export async function verifyPackageArchiveSignature({ if (!pkgArchiveSignature) { logger.warn(`Not performing package verification as package has no signature file`); - return { didVerify: false }; + return { verificationAttempted: false }; } const { archiveBuffer: pkgArchiveBuffer } = await Registry.fetchArchiveBuffer( @@ -94,7 +96,7 @@ export async function verifyPackageArchiveSignature({ logger, }); - return { didVerify: true, ...verificationResult }; + return { verificationAttempted: true, ...verificationResult }; } async function _verifyPackageSignature({ From f60ca91812ad2308b9c838c52b5f650947bf8a9e Mon Sep 17 00:00:00 2001 From: Mark Hopkin Date: Wed, 29 Jun 2022 11:07:22 +0100 Subject: [PATCH 05/18] update installation object with verify result --- .../services/epm/packages/_install_package.ts | 13 ++++--- .../server/services/epm/packages/install.ts | 39 +++++++++++++------ .../epm/packages/package_verification.ts | 16 ++++++++ 3 files changed, 51 insertions(+), 17 deletions(-) diff --git a/x-pack/plugins/fleet/server/services/epm/packages/_install_package.ts b/x-pack/plugins/fleet/server/services/epm/packages/_install_package.ts index ffc36b87cb41e..faee321f89336 100644 --- a/x-pack/plugins/fleet/server/services/epm/packages/_install_package.ts +++ b/x-pack/plugins/fleet/server/services/epm/packages/_install_package.ts @@ -39,7 +39,7 @@ import { saveArchiveEntries } from '../archive/storage'; import { ConcurrentInstallOperationError } from '../../../errors'; import { packagePolicyService } from '../..'; -import { createInstallation, updateEsAssetReferences } from './install'; +import { createInstallation, updateEsAssetReferences, restartInstallation } from './install'; import { withPackageSpan } from './utils'; import type { PackageVerificationResult } from './package_verification'; @@ -91,11 +91,12 @@ export async function _installPackage({ } else { // if no installation is running, or the installation has been running longer than MAX_TIME_COMPLETE_INSTALL // (it might be stuck) update the saved object and proceed - await savedObjectsClient.update(PACKAGES_SAVED_OBJECT_TYPE, pkgName, { - install_version: pkgVersion, - install_status: 'installing', - install_started_at: new Date().toISOString(), - install_source: installSource, + await restartInstallation({ + savedObjectsClient, + pkgName, + pkgVersion, + installSource, + verificationResult, }); } } else { diff --git a/x-pack/plugins/fleet/server/services/epm/packages/install.ts b/x-pack/plugins/fleet/server/services/epm/packages/install.ts index ee2614f94975d..b271569468ee1 100644 --- a/x-pack/plugins/fleet/server/services/epm/packages/install.ts +++ b/x-pack/plugins/fleet/server/services/epm/packages/install.ts @@ -52,7 +52,10 @@ import type { PackageUpdateEvent } from '../../upgrade_sender'; import { sendTelemetryEvents, UpdateEventType } from '../../upgrade_sender'; import type { PackageVerificationResult } from './package_verification'; -import { verifyPackageArchiveSignature } from './package_verification'; +import { + verifyPackageArchiveSignature, + formatVerificationResultForSO, +} from './package_verification'; import { getInstallation, getInstallationObject } from '.'; import { removeInstallation } from './remove'; @@ -601,6 +604,29 @@ export const updateInstallStatus = async ({ }); }; +export async function restartInstallation(options: { + savedObjectsClient: SavedObjectsClientContract; + pkgName: string; + pkgVersion: string; + installSource: InstallSource; + verificationResult?: PackageVerificationResult; +}) { + const { savedObjectsClient, pkgVersion, pkgName, installSource, verificationResult } = options; + + const savedObjectUpdate: Partial = { + install_version: pkgVersion, + install_status: 'installing', + install_started_at: new Date().toISOString(), + install_source: installSource, + }; + + if (verificationResult) { + savedObjectUpdate.verification = formatVerificationResultForSO(verificationResult); + } + + await savedObjectsClient.update(PACKAGES_SAVED_OBJECT_TYPE, pkgName, savedObjectUpdate); +} + export async function createInstallation(options: { savedObjectsClient: SavedObjectsClientContract; packageInfo: InstallablePackage; @@ -638,16 +664,7 @@ export async function createInstallation(options: { }; if (verificationResult) { - const verification: Installation['verification'] = { - verification_attempted: verificationResult.verificationAttempted, - }; - - if (verificationResult.verificationAttempted) { - verification.is_verified = verificationResult.isVerified; - verification.key_id = verificationResult.keyId; - } - - savedObject.verification = verification; + savedObject.verification = formatVerificationResultForSO(verificationResult); } const created = await savedObjectsClient.create( diff --git a/x-pack/plugins/fleet/server/services/epm/packages/package_verification.ts b/x-pack/plugins/fleet/server/services/epm/packages/package_verification.ts index 8234da5812783..441b381d40adb 100644 --- a/x-pack/plugins/fleet/server/services/epm/packages/package_verification.ts +++ b/x-pack/plugins/fleet/server/services/epm/packages/package_verification.ts @@ -13,6 +13,7 @@ import type { Logger } from '@kbn/logging'; import * as Registry from '../registry'; import { appContextService } from '../../app_context'; +import type { Installation } from '../../../types'; interface VerificationResult { isVerified: boolean; @@ -134,3 +135,18 @@ async function _verifyPackageSignature({ } return { isVerified, keyId: verificationKey.getKeyID().toHex() }; } + +export function formatVerificationResultForSO( + verificationResult: PackageVerificationResult +): Installation['verification'] { + const verification: Installation['verification'] = { + verification_attempted: verificationResult.verificationAttempted, + }; + + if (verificationResult.verificationAttempted) { + verification.is_verified = verificationResult.isVerified; + verification.key_id = verificationResult.keyId; + } + + return verification; +} From 065a07816408718343a2a78f36c1c1e1557e0e1c Mon Sep 17 00:00:00 2001 From: Mark Hopkin Date: Wed, 29 Jun 2022 13:42:53 +0100 Subject: [PATCH 06/18] move to verification_status --- .../.storybook/context/fixtures/packages.ts | 6 ++++ .../plugins/fleet/common/types/models/epm.ts | 8 ++--- .../epm/components/package_card.stories.tsx | 1 + .../epm/screens/detail/index.test.tsx | 3 +- .../epm/packages/get_install_type.test.ts | 2 ++ .../server/services/epm/packages/install.ts | 13 ++++--- .../epm/packages/package_verification.ts | 35 +++++++++++-------- .../common/endpoint/generate_data.ts | 1 + .../routes/actions/response_actions.test.ts | 1 + .../endpoint/routes/metadata/metadata.test.ts | 1 + 10 files changed, 46 insertions(+), 25 deletions(-) diff --git a/x-pack/plugins/fleet/.storybook/context/fixtures/packages.ts b/x-pack/plugins/fleet/.storybook/context/fixtures/packages.ts index a3e436b3f9718..cb902de21a278 100644 --- a/x-pack/plugins/fleet/.storybook/context/fixtures/packages.ts +++ b/x-pack/plugins/fleet/.storybook/context/fixtures/packages.ts @@ -63,6 +63,7 @@ export const items: GetPackagesResponse['items'] = [ install_started_at: '2021-08-25T19:44:37.078Z', install_source: 'registry', keep_policies_up_to_date: false, + verification_status: 'unknown', }, references: [], coreMigrationVersion: '7.14.0', @@ -100,6 +101,7 @@ export const items: GetPackagesResponse['items'] = [ install_started_at: '2021-08-25T19:44:37.078Z', install_source: 'registry', keep_policies_up_to_date: false, + verification_status: 'unknown', }, references: [], coreMigrationVersion: '7.14.0', @@ -162,6 +164,7 @@ export const items: GetPackagesResponse['items'] = [ install_started_at: '2021-08-25T19:44:37.078Z', install_source: 'registry', keep_policies_up_to_date: false, + verification_status: 'unknown', }, references: [], coreMigrationVersion: '7.14.0', @@ -199,6 +202,7 @@ export const items: GetPackagesResponse['items'] = [ install_started_at: '2021-08-25T19:44:37.078Z', install_source: 'registry', keep_policies_up_to_date: false, + verification_status: 'unknown', }, references: [], coreMigrationVersion: '7.14.0', @@ -270,6 +274,7 @@ export const items: GetPackagesResponse['items'] = [ install_started_at: '2021-08-25T19:44:37.078Z', install_source: 'registry', keep_policies_up_to_date: false, + verification_status: 'unknown', }, references: [], coreMigrationVersion: '7.14.0', @@ -307,6 +312,7 @@ export const items: GetPackagesResponse['items'] = [ install_started_at: '2021-08-25T19:44:37.078Z', install_source: 'registry', keep_policies_up_to_date: false, + verification_status: 'unknown', }, references: [], coreMigrationVersion: '7.14.0', diff --git a/x-pack/plugins/fleet/common/types/models/epm.ts b/x-pack/plugins/fleet/common/types/models/epm.ts index a56027a570251..7bc5dae209c4d 100644 --- a/x-pack/plugins/fleet/common/types/models/epm.ts +++ b/x-pack/plugins/fleet/common/types/models/epm.ts @@ -401,6 +401,7 @@ export interface IntegrationCardItem { fromIntegrations?: string; } +export type PackageVerificationStatus = 'verified' | 'unverified' | 'unknown'; export type PackagesGroupedByStatus = Record, PackageList>; export type PackageInfo = | Installable> @@ -420,11 +421,8 @@ export interface Installation extends SavedObjectAttributes { installed_kibana_space_id?: string; keep_policies_up_to_date?: boolean; install_format_schema_version?: string; - verification?: { - verification_attempted?: boolean; - is_verified?: boolean; - key_id?: string; - }; + verification_status: PackageVerificationStatus; + verification_key_id?: string; } export interface PackageUsageStats { diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/package_card.stories.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/package_card.stories.tsx index ff98fb988d04b..e62044b31333a 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/package_card.stories.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/package_card.stories.tsx @@ -69,6 +69,7 @@ export const Installed = ({ width, ...props }: Args) => { install_source: 'registry', install_started_at: '2020-01-01T00:00:00.000Z', keep_policies_up_to_date: false, + verification_status: 'unknown', }, references: [], }; diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/index.test.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/index.test.tsx index 9195e367030dd..a77c45a6b5110 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/index.test.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/index.test.tsx @@ -34,7 +34,8 @@ import { createIntegrationsTestRendererMock } from '../../../../../../mock'; import { ExperimentalFeaturesService } from '../../../../services'; -ExperimentalFeaturesService.init({ createPackagePolicyMultiPageLayout: false }); +// @ts-ignore this saves us having to define all experimental features +ExperimentalFeaturesService.init({}); import { Detail } from '.'; describe('when on integration detail', () => { diff --git a/x-pack/plugins/fleet/server/services/epm/packages/get_install_type.test.ts b/x-pack/plugins/fleet/server/services/epm/packages/get_install_type.test.ts index e67f59aaba55b..6c4211d8cf2fe 100644 --- a/x-pack/plugins/fleet/server/services/epm/packages/get_install_type.test.ts +++ b/x-pack/plugins/fleet/server/services/epm/packages/get_install_type.test.ts @@ -30,6 +30,7 @@ const mockInstallation: SavedObject = { install_started_at: new Date().toISOString(), install_source: 'registry', keep_policies_up_to_date: false, + verification_status: 'unknown', }, }; const mockInstallationUpdateFail: SavedObject = { @@ -50,6 +51,7 @@ const mockInstallationUpdateFail: SavedObject = { install_started_at: new Date().toISOString(), install_source: 'registry', keep_policies_up_to_date: false, + verification_status: 'unknown', }, }; diff --git a/x-pack/plugins/fleet/server/services/epm/packages/install.ts b/x-pack/plugins/fleet/server/services/epm/packages/install.ts index b271569468ee1..23ab377cd3682 100644 --- a/x-pack/plugins/fleet/server/services/epm/packages/install.ts +++ b/x-pack/plugins/fleet/server/services/epm/packages/install.ts @@ -357,9 +357,8 @@ async function installPackageFromRegistry({ pkgVersion, logger, }); - const isUnverified = - verificationResult.verificationAttempted && !verificationResult.isVerified; - if (isUnverified && !force) { + + if (verificationResult.verificationStatus === 'unverified' && !force) { throw new PackageFailedVerificationError(pkgkey); } } @@ -613,7 +612,7 @@ export async function restartInstallation(options: { }) { const { savedObjectsClient, pkgVersion, pkgName, installSource, verificationResult } = options; - const savedObjectUpdate: Partial = { + let savedObjectUpdate: Partial = { install_version: pkgVersion, install_status: 'installing', install_started_at: new Date().toISOString(), @@ -621,7 +620,10 @@ export async function restartInstallation(options: { }; if (verificationResult) { - savedObjectUpdate.verification = formatVerificationResultForSO(verificationResult); + savedObjectUpdate = { + ...savedObjectUpdate, + ...formatVerificationResultForSO(verificationResult), + }; } await savedObjectsClient.update(PACKAGES_SAVED_OBJECT_TYPE, pkgName, savedObjectUpdate); @@ -661,6 +663,7 @@ export async function createInstallation(options: { install_source: installSource, install_format_schema_version: FLEET_INSTALL_FORMAT_VERSION, keep_policies_up_to_date: defaultKeepPoliciesUpToDate, + verification_status: 'unknown', }; if (verificationResult) { diff --git a/x-pack/plugins/fleet/server/services/epm/packages/package_verification.ts b/x-pack/plugins/fleet/server/services/epm/packages/package_verification.ts index 441b381d40adb..242242e0c071f 100644 --- a/x-pack/plugins/fleet/server/services/epm/packages/package_verification.ts +++ b/x-pack/plugins/fleet/server/services/epm/packages/package_verification.ts @@ -10,6 +10,8 @@ import { readFile } from 'fs/promises'; import * as openpgp from 'openpgp'; import type { Logger } from '@kbn/logging'; +import type { PackageVerificationStatus } from '../../../../common'; + import * as Registry from '../registry'; import { appContextService } from '../../app_context'; @@ -20,9 +22,10 @@ interface VerificationResult { keyId: string; } -export type PackageVerificationResult = - | { verificationAttempted: false } - | ({ verificationAttempted: true } & VerificationResult); +export interface PackageVerificationResult { + verificationKeyId?: string; + verificationStatus: PackageVerificationStatus; +} let cachedKey: openpgp.Key | undefined | null = null; @@ -69,10 +72,10 @@ export async function verifyPackageArchiveSignature({ logger: Logger; }): Promise { const verificationKey = await getGpgKeyOrUndefined(); - + const result: PackageVerificationResult = { verificationStatus: 'unknown' }; if (!verificationKey) { logger.warn(`Not performing package verification as no local verification key found`); - return { verificationAttempted: false }; + return result; } const pkgArchiveSignature = await Registry.getPackageArchiveSignatureOrUndefined({ pkgName, @@ -82,7 +85,7 @@ export async function verifyPackageArchiveSignature({ if (!pkgArchiveSignature) { logger.warn(`Not performing package verification as package has no signature file`); - return { verificationAttempted: false }; + return result; } const { archiveBuffer: pkgArchiveBuffer } = await Registry.fetchArchiveBuffer( @@ -90,14 +93,14 @@ export async function verifyPackageArchiveSignature({ pkgVersion ); - const verificationResult = await _verifyPackageSignature({ + const { isVerified, keyId } = await _verifyPackageSignature({ pkgArchiveBuffer, pkgArchiveSignature, verificationKey, logger, }); - return { verificationAttempted: true, ...verificationResult }; + return { verificationStatus: isVerified ? 'verified' : 'unverified', verificationKeyId: keyId }; } async function _verifyPackageSignature({ @@ -136,16 +139,20 @@ async function _verifyPackageSignature({ return { isVerified, keyId: verificationKey.getKeyID().toHex() }; } +type InstallationVerificationKeys = Pick< + Installation, + 'verification_status' | 'verification_key_id' +>; + export function formatVerificationResultForSO( verificationResult: PackageVerificationResult -): Installation['verification'] { - const verification: Installation['verification'] = { - verification_attempted: verificationResult.verificationAttempted, +): InstallationVerificationKeys { + const verification: InstallationVerificationKeys = { + verification_status: verificationResult.verificationStatus, }; - if (verificationResult.verificationAttempted) { - verification.is_verified = verificationResult.isVerified; - verification.key_id = verificationResult.keyId; + if (verificationResult.verificationKeyId) { + verification.verification_key_id = verificationResult.verificationKeyId; } return verification; diff --git a/x-pack/plugins/security_solution/common/endpoint/generate_data.ts b/x-pack/plugins/security_solution/common/endpoint/generate_data.ts index 35eb9de6d4060..c817c53b7658f 100644 --- a/x-pack/plugins/security_solution/common/endpoint/generate_data.ts +++ b/x-pack/plugins/security_solution/common/endpoint/generate_data.ts @@ -1769,6 +1769,7 @@ export class EndpointDocGenerator extends BaseDataGenerator { install_started_at: '2020-06-24T14:41:23.098Z', install_source: 'registry', keep_policies_up_to_date: false, + verification_status: 'unknown', }, references: [], updated_at: '2020-06-24T14:41:23.098Z', diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/actions/response_actions.test.ts b/x-pack/plugins/security_solution/server/endpoint/routes/actions/response_actions.test.ts index 2d5790d09a8f4..e4c66d5ca0538 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/actions/response_actions.test.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/actions/response_actions.test.ts @@ -131,6 +131,7 @@ describe('Response actions', () => { }, ], keep_policies_up_to_date: false, + verification_status: 'unknown', }); licenseEmitter = new Subject(); diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/metadata/metadata.test.ts b/x-pack/plugins/security_solution/server/endpoint/routes/metadata/metadata.test.ts index 628672b13006e..39f1890df7ca9 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/metadata/metadata.test.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/metadata/metadata.test.ts @@ -142,6 +142,7 @@ describe('test endpoint routes', () => { }, ], keep_policies_up_to_date: false, + verification_status: 'unknown', }); endpointAppContextService.setup(createMockEndpointAppContextServiceSetupContract()); endpointAppContextService.start({ ...startContract, packageService: mockPackageService }); From 512b2b1800706b3350f30aa01295c5ea01b71410 Mon Sep 17 00:00:00 2001 From: Mark Hopkin Date: Wed, 29 Jun 2022 17:23:07 +0100 Subject: [PATCH 07/18] store verification status in cache --- .../validate_bundled_packages.test.ts | 9 +- .../server/services/epm/archive/cache.ts | 18 +++- .../server/services/epm/archive/index.ts | 4 +- .../server/services/epm/package_service.ts | 8 +- .../fleet/server/services/epm/packages/get.ts | 10 +- .../server/services/epm/packages/install.ts | 31 ++---- .../epm/packages/package_verification.ts | 7 +- .../server/services/epm/registry/index.ts | 96 ++++++++++++------- 8 files changed, 109 insertions(+), 74 deletions(-) diff --git a/x-pack/plugins/fleet/server/integration_tests/validate_bundled_packages.test.ts b/x-pack/plugins/fleet/server/integration_tests/validate_bundled_packages.test.ts index fa7f8b2275dad..e59d5d0f5f109 100644 --- a/x-pack/plugins/fleet/server/integration_tests/validate_bundled_packages.test.ts +++ b/x-pack/plugins/fleet/server/integration_tests/validate_bundled_packages.test.ts @@ -46,10 +46,11 @@ describe('validate bundled packages', () => { bundledPackage.version ); - const packageArchive = await Registry.fetchArchiveBuffer( - bundledPackage.name, - bundledPackage.version - ); + const packageArchive = await Registry.fetchArchiveBuffer({ + pkgName: bundledPackage.name, + pkgVersion: bundledPackage.version, + verify: true, + }); return { registryPackage, packageArchive }; }) diff --git a/x-pack/plugins/fleet/server/services/epm/archive/cache.ts b/x-pack/plugins/fleet/server/services/epm/archive/cache.ts index ea94823287735..31778387d14e0 100644 --- a/x-pack/plugins/fleet/server/services/epm/archive/cache.ts +++ b/x-pack/plugins/fleet/server/services/epm/archive/cache.ts @@ -6,10 +6,15 @@ */ import type { ArchivePackage, RegistryPackage } from '../../../../common'; +import type { PackageVerificationResult } from '../packages/package_verification'; import { appContextService } from '../..'; import type { ArchiveEntry } from '.'; +type SharedKeyString = string; + +const sharedKey = ({ name, version }: SharedKey): SharedKeyString => `${name}-${version}`; + const archiveEntryCache: Map = new Map(); export const getArchiveEntry = (key: string) => archiveEntryCache.get(key); export const setArchiveEntry = (key: string, value: Buffer) => archiveEntryCache.set(key, value); @@ -17,11 +22,21 @@ export const hasArchiveEntry = (key: string) => archiveEntryCache.has(key); export const clearArchiveEntries = () => archiveEntryCache.clear(); export const deleteArchiveEntry = (key: string) => archiveEntryCache.delete(key); +const verificationResultCache: Map = new Map(); +export const getVerificationResult = (key: SharedKey) => + verificationResultCache.get(sharedKey(key)); +export const setVerificationResult = (key: SharedKey, value: PackageVerificationResult) => + verificationResultCache.set(sharedKey(key), value); +export const hasVerificationResult = (key: SharedKey) => + verificationResultCache.has(sharedKey(key)); +export const clearVerificationResults = () => verificationResultCache.clear(); +export const deleteVerificationResult = (key: SharedKey) => + verificationResultCache.delete(sharedKey(key)); + export interface SharedKey { name: string; version: string; } -type SharedKeyString = string; const archiveFilelistCache: Map = new Map(); export const getArchiveFilelist = (keyArgs: SharedKey) => @@ -38,7 +53,6 @@ export const deleteArchiveFilelist = (keyArgs: SharedKey) => archiveFilelistCache.delete(sharedKey(keyArgs)); const packageInfoCache: Map = new Map(); -const sharedKey = ({ name, version }: SharedKey) => `${name}-${version}`; export const getPackageInfo = (args: SharedKey) => { return packageInfoCache.get(sharedKey(args)); diff --git a/x-pack/plugins/fleet/server/services/epm/archive/index.ts b/x-pack/plugins/fleet/server/services/epm/archive/index.ts index e7e43d3d932b7..4d70c62bd55d3 100644 --- a/x-pack/plugins/fleet/server/services/epm/archive/index.ts +++ b/x-pack/plugins/fleet/server/services/epm/archive/index.ts @@ -5,7 +5,7 @@ * 2.0. */ -import type { AssetParts, InstallSource } from '../../../../common/types'; +import type { AssetParts } from '../../../../common/types'; import { PackageInvalidArchiveError, PackageUnsupportedMediaTypeError } from '../../../errors'; import { @@ -35,13 +35,11 @@ export async function unpackBufferToCache({ version, contentType, archiveBuffer, - installSource, }: { name: string; version: string; contentType: string; archiveBuffer: Buffer; - installSource: InstallSource; }): Promise { // Make sure any buffers from previous installations from registry or upload are deleted first clearPackageFileCache({ name, version }); diff --git a/x-pack/plugins/fleet/server/services/epm/package_service.ts b/x-pack/plugins/fleet/server/services/epm/package_service.ts index e16d4954f0b9d..c43386acdbdf4 100644 --- a/x-pack/plugins/fleet/server/services/epm/package_service.ts +++ b/x-pack/plugins/fleet/server/services/epm/package_service.ts @@ -120,9 +120,13 @@ class PackageClientImpl implements PackageClient { return fetchFindLatestPackageOrThrow(packageName); } - public async getRegistryPackage(packageName: string, packageVersion: string) { + public async getRegistryPackage( + packageName: string, + packageVersion: string, + options?: Parameters['2'] + ) { await this.#runPreflight(); - return getRegistryPackage(packageName, packageVersion); + return getRegistryPackage(packageName, packageVersion, options); } public async reinstallEsAssets( diff --git a/x-pack/plugins/fleet/server/services/epm/packages/get.ts b/x-pack/plugins/fleet/server/services/epm/packages/get.ts index acd5761919a16..461b7241fe167 100644 --- a/x-pack/plugins/fleet/server/services/epm/packages/get.ts +++ b/x-pack/plugins/fleet/server/services/epm/packages/get.ts @@ -22,7 +22,11 @@ import type { GetCategoriesRequest, } from '../../../../common/types'; import type { Installation, PackageInfo } from '../../../types'; -import { IngestManagerError, PackageNotFoundError } from '../../../errors'; +import { + IngestManagerError, + PackageFailedVerificationError, + PackageNotFoundError, +} from '../../../errors'; import { appContextService } from '../..'; import * as Registry from '../registry'; import { getEsPackage } from '../archive/storage'; @@ -268,8 +272,10 @@ export async function getPackageFromSource(options: { try { res = await Registry.getRegistryPackage(pkgName, pkgVersion); logger.debug(`retrieved installed package ${pkgName}-${pkgVersion} from registry`); - // TODO: add to cache and storage here? } catch (error) { + if (error instanceof PackageFailedVerificationError) { + throw error; + } // treating this is a 404 as no status code returned // in the unlikely event its missing from cache, storage, and never installed from registry } diff --git a/x-pack/plugins/fleet/server/services/epm/packages/install.ts b/x-pack/plugins/fleet/server/services/epm/packages/install.ts index 23ab377cd3682..5dd1d7dbdd417 100644 --- a/x-pack/plugins/fleet/server/services/epm/packages/install.ts +++ b/x-pack/plugins/fleet/server/services/epm/packages/install.ts @@ -29,11 +29,7 @@ import type { InstallSource, } from '../../../../common'; import { AUTO_UPGRADE_POLICIES_PACKAGES } from '../../../../common'; -import { - IngestManagerError, - PackageOutdatedError, - PackageFailedVerificationError, -} from '../../../errors'; +import { IngestManagerError, PackageOutdatedError } from '../../../errors'; import { PACKAGES_SAVED_OBJECT_TYPE, MAX_TIME_COMPLETE_INSTALL } from '../../../constants'; import type { KibanaAssetType } from '../../../types'; import { licenseService } from '../..'; @@ -44,6 +40,7 @@ import { setPackageInfo, generatePackageInfoFromArchiveBuffer, unpackBufferToCache, + deleteVerificationResult, } from '../archive'; import { toAssetReference } from '../kibana/assets/install'; import type { ArchiveAsset } from '../kibana/assets/install'; @@ -52,10 +49,7 @@ import type { PackageUpdateEvent } from '../../upgrade_sender'; import { sendTelemetryEvents, UpdateEventType } from '../../upgrade_sender'; import type { PackageVerificationResult } from './package_verification'; -import { - verifyPackageArchiveSignature, - formatVerificationResultForSO, -} from './package_verification'; +import { formatVerificationResultForSO } from './package_verification'; import { getInstallation, getInstallationObject } from '.'; import { removeInstallation } from './remove'; @@ -287,11 +281,11 @@ async function installPackageFromRegistry({ }); // get latest package version and requested version in parallel for performance - const [latestPackage, { paths, packageInfo }] = await Promise.all([ + const [latestPackage, { paths, packageInfo, verificationResult }] = await Promise.all([ Registry.fetchFindLatestPackageOrThrow(pkgName, { ignoreConstraints, }), - Registry.getRegistryPackage(pkgName, pkgVersion), + Registry.getRegistryPackage(pkgName, pkgVersion, { ignoreUnverified: force }), ]); // let the user install if using the force flag or needing to reinstall or install a previous version due to failed update @@ -350,18 +344,6 @@ async function installPackageFromRegistry({ .getSavedObjects() .createImporter(savedObjectsClient); - let verificationResult; - if (appContextService.getExperimentalFeatures().packageVerification) { - verificationResult = await verifyPackageArchiveSignature({ - pkgName, - pkgVersion, - logger, - }); - - if (verificationResult.verificationStatus === 'unverified' && !force) { - throw new PackageFailedVerificationError(pkgkey); - } - } // try installing the package, if there was an error, call error handler and rethrow // @ts-expect-error status is string instead of InstallResult.status 'installed' | 'already_installed' return await _installPackage({ @@ -458,10 +440,11 @@ async function installPackageByUpload({ telemetryEvent.currentVersion = installedPkg?.attributes.version || 'not_installed'; const installSource = 'upload'; + // as we do not verify uploaded packages, we must set the + deleteVerificationResult(packageInfo); const paths = await unpackBufferToCache({ name: packageInfo.name, version: packageInfo.version, - installSource, archiveBuffer, contentType, }); diff --git a/x-pack/plugins/fleet/server/services/epm/packages/package_verification.ts b/x-pack/plugins/fleet/server/services/epm/packages/package_verification.ts index 242242e0c071f..fce47d99d4ab2 100644 --- a/x-pack/plugins/fleet/server/services/epm/packages/package_verification.ts +++ b/x-pack/plugins/fleet/server/services/epm/packages/package_verification.ts @@ -65,10 +65,12 @@ export async function _readGpgKey(): Promise { export async function verifyPackageArchiveSignature({ pkgName, pkgVersion, + pkgArchiveBuffer, logger, }: { pkgName: string; pkgVersion: string; + pkgArchiveBuffer: Buffer; logger: Logger; }): Promise { const verificationKey = await getGpgKeyOrUndefined(); @@ -88,11 +90,6 @@ export async function verifyPackageArchiveSignature({ return result; } - const { archiveBuffer: pkgArchiveBuffer } = await Registry.fetchArchiveBuffer( - pkgName, - pkgVersion - ); - const { isVerified, keyId } = await _verifyPackageSignature({ pkgArchiveBuffer, pkgArchiveSignature, diff --git a/x-pack/plugins/fleet/server/services/epm/registry/index.ts b/x-pack/plugins/fleet/server/services/epm/registry/index.ts index e690efce1c69f..8bd710a252113 100644 --- a/x-pack/plugins/fleet/server/services/epm/registry/index.ts +++ b/x-pack/plugins/fleet/server/services/epm/registry/index.ts @@ -20,7 +20,6 @@ import type { AssetsGroupedByServiceByType, CategoryId, CategorySummaryList, - InstallSource, RegistryPackage, RegistrySearchResults, GetCategoriesRequest, @@ -29,17 +28,26 @@ import { getArchiveFilelist, getPathParts, unpackBufferToCache, + setVerificationResult, + getVerificationResult, getPackageInfo, setPackageInfo, } from '../archive'; import { streamToBuffer, streamToString } from '../streams'; import { appContextService } from '../..'; -import { PackageNotFoundError, PackageCacheError, RegistryResponseError } from '../../../errors'; +import { + PackageNotFoundError, + RegistryResponseError, + PackageFailedVerificationError, +} from '../../../errors'; import { getBundledPackageByName } from '../packages/bundled_packages'; import { withPackageSpan } from '../packages/utils'; +import type { PackageVerificationResult } from '../packages/package_verification'; +import { verifyPackageArchiveSignature } from '../packages/package_verification'; + import { fetchUrl, getResponse, getResponseStream } from './requests'; import { getRegistryUrl } from './registry_url'; @@ -225,20 +233,37 @@ export async function getInfo(name: string, version: string) { export async function getRegistryPackage( name: string, - version: string -): Promise<{ paths: string[]; packageInfo: RegistryPackage }> { - const installSource = 'registry'; + version: string, + options?: { ignoreUnverified?: boolean } +): Promise<{ + paths: string[]; + packageInfo: RegistryPackage; + verificationResult?: PackageVerificationResult; +}> { + const verifyPackage = appContextService.getExperimentalFeatures().packageVerification; let paths = getArchiveFilelist({ name, version }); + let verificationResult = getVerificationResult({ name, version }); if (!paths || paths.length === 0) { - const { archiveBuffer, archivePath } = await withPackageSpan( - 'Fetch package archive from registry', - () => fetchArchiveBuffer(name, version) + const { + archiveBuffer, + archivePath, + verificationResult: latestVerificationResult, + } = await withPackageSpan('Fetch package archive from registry', () => + fetchArchiveBuffer({ + pkgName: name, + pkgVersion: version, + verify: verifyPackage, + ignoreUnverified: options?.ignoreUnverified, + }) ); + if (latestVerificationResult) { + verificationResult = latestVerificationResult; + setVerificationResult({ name, version }, latestVerificationResult); + } paths = await withPackageSpan('Unpack archive', () => unpackBufferToCache({ name, version, - installSource, archiveBuffer, contentType: ensureContentType(archivePath), }) @@ -246,7 +271,7 @@ export async function getRegistryPackage( } const packageInfo = await getInfo(name, version); - return { paths, packageInfo }; + return { paths, packageInfo, verificationResult }; } function ensureContentType(archivePath: string) { @@ -257,31 +282,38 @@ function ensureContentType(archivePath: string) { return contentType; } -export async function ensureCachedArchiveInfo( - name: string, - version: string, - installSource: InstallSource = 'registry' -) { - const paths = getArchiveFilelist({ name, version }); - if (!paths || paths.length === 0) { - if (installSource === 'registry') { - await getRegistryPackage(name, version); - } else { - throw new PackageCacheError( - `Package ${name}-${version} not cached. If it was uploaded, try uninstalling and reinstalling manually.` - ); - } - } -} - -export async function fetchArchiveBuffer( - pkgName: string, - pkgVersion: string -): Promise<{ archiveBuffer: Buffer; archivePath: string }> { +export async function fetchArchiveBuffer({ + pkgName, + pkgVersion, + verify, + ignoreUnverified = false, +}: { + pkgName: string; + pkgVersion: string; + verify: boolean; + ignoreUnverified?: boolean; +}): Promise<{ + archiveBuffer: Buffer; + archivePath: string; + verificationResult?: PackageVerificationResult; +}> { + const logger = appContextService.getLogger(); const { download: archivePath } = await getInfo(pkgName, pkgVersion); const archiveUrl = `${getRegistryUrl()}${archivePath}`; const archiveBuffer = await getResponseStream(archiveUrl).then(streamToBuffer); - + if (verify) { + const verificationResult = await verifyPackageArchiveSignature({ + pkgName, + pkgVersion, + pkgArchiveBuffer: archiveBuffer, + logger, + }); + + if (verificationResult.verificationStatus === 'unverified' && !ignoreUnverified) { + throw new PackageFailedVerificationError(`${pkgName}-${pkgVersion}`); + } + return { archiveBuffer, archivePath, verificationResult }; + } return { archiveBuffer, archivePath }; } From d6b38a26d1c4f39890311aaad19d78b0c11d479e Mon Sep 17 00:00:00 2001 From: Mark Hopkin Date: Wed, 29 Jun 2022 20:09:44 +0100 Subject: [PATCH 08/18] send 422 if verification fails --- x-pack/plugins/fleet/server/errors/handlers.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/x-pack/plugins/fleet/server/errors/handlers.ts b/x-pack/plugins/fleet/server/errors/handlers.ts index eea3ac5c35f5f..3d92432658751 100644 --- a/x-pack/plugins/fleet/server/errors/handlers.ts +++ b/x-pack/plugins/fleet/server/errors/handlers.ts @@ -28,6 +28,7 @@ import { RegistryConnectionError, RegistryError, RegistryResponseError, + PackageFailedVerificationError, } from '.'; type IngestErrorHandler = ( @@ -60,6 +61,9 @@ const getHTTPResponseCode = (error: IngestManagerError): number => { if (error instanceof PackageUnsupportedMediaTypeError) { return 415; // Unsupported Media Type } + if (error instanceof PackageFailedVerificationError) { + return 422; // Unprocessable Entity + } if (error instanceof ConcurrentInstallOperationError) { return 409; // Conflict } From f855e2c76901b10cafe3089dcc413be8d9cc8b74 Mon Sep 17 00:00:00 2001 From: Mark Hopkin Date: Thu, 30 Jun 2022 14:53:11 +0100 Subject: [PATCH 09/18] only get cached verification result if verify = true --- x-pack/plugins/fleet/server/services/epm/registry/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/fleet/server/services/epm/registry/index.ts b/x-pack/plugins/fleet/server/services/epm/registry/index.ts index 8bd710a252113..d2d468a2afd2a 100644 --- a/x-pack/plugins/fleet/server/services/epm/registry/index.ts +++ b/x-pack/plugins/fleet/server/services/epm/registry/index.ts @@ -242,7 +242,7 @@ export async function getRegistryPackage( }> { const verifyPackage = appContextService.getExperimentalFeatures().packageVerification; let paths = getArchiveFilelist({ name, version }); - let verificationResult = getVerificationResult({ name, version }); + let verificationResult = verifyPackage ? getVerificationResult({ name, version }) : undefined; if (!paths || paths.length === 0) { const { archiveBuffer, From f92e02b3032bd917a394bdd6961d62bf828522a1 Mon Sep 17 00:00:00 2001 From: Mark Hopkin Date: Thu, 30 Jun 2022 15:02:05 +0100 Subject: [PATCH 10/18] correctly set verififcation status keys --- x-pack/plugins/fleet/server/services/epm/packages/install.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/fleet/server/services/epm/packages/install.ts b/x-pack/plugins/fleet/server/services/epm/packages/install.ts index 5dd1d7dbdd417..1a390df64d241 100644 --- a/x-pack/plugins/fleet/server/services/epm/packages/install.ts +++ b/x-pack/plugins/fleet/server/services/epm/packages/install.ts @@ -632,7 +632,7 @@ export async function createInstallation(options: { ? true : undefined; - const savedObject: Installation = { + let savedObject: Installation = { installed_kibana: [], installed_kibana_space_id: options.spaceId, installed_es: [], @@ -650,7 +650,7 @@ export async function createInstallation(options: { }; if (verificationResult) { - savedObject.verification = formatVerificationResultForSO(verificationResult); + savedObject = { ...savedObject, ...formatVerificationResultForSO(verificationResult) }; } const created = await savedObjectsClient.create( From 31550b6f5bafaae332e948527f3b63a2646c32ba Mon Sep 17 00:00:00 2001 From: Mark Hopkin Date: Thu, 30 Jun 2022 15:51:11 +0100 Subject: [PATCH 11/18] unset verification key ID when restarting installation --- x-pack/plugins/fleet/common/types/models/epm.ts | 2 +- x-pack/plugins/fleet/server/saved_objects/index.ts | 10 ++-------- .../fleet/server/services/epm/packages/install.ts | 1 + 3 files changed, 4 insertions(+), 9 deletions(-) diff --git a/x-pack/plugins/fleet/common/types/models/epm.ts b/x-pack/plugins/fleet/common/types/models/epm.ts index 7bc5dae209c4d..890149640be78 100644 --- a/x-pack/plugins/fleet/common/types/models/epm.ts +++ b/x-pack/plugins/fleet/common/types/models/epm.ts @@ -422,7 +422,7 @@ export interface Installation extends SavedObjectAttributes { keep_policies_up_to_date?: boolean; install_format_schema_version?: string; verification_status: PackageVerificationStatus; - verification_key_id?: string; + verification_key_id?: string | null; } export interface PackageUsageStats { diff --git a/x-pack/plugins/fleet/server/saved_objects/index.ts b/x-pack/plugins/fleet/server/saved_objects/index.ts index 0b64402391453..bc52fa2e2017a 100644 --- a/x-pack/plugins/fleet/server/saved_objects/index.ts +++ b/x-pack/plugins/fleet/server/saved_objects/index.ts @@ -231,14 +231,8 @@ const getSavedObjectTypes = ( enabled: false, type: 'object', }, - verification: { - type: 'object', - properties: { - verification_attempted: { type: 'boolean' }, - is_verified: { type: 'boolean' }, - key_id: { type: 'keyword' }, - }, - }, + verification_status: { type: 'keyword' }, + verification_key_id: { type: 'keyword' }, installed_es: { type: 'nested', properties: { diff --git a/x-pack/plugins/fleet/server/services/epm/packages/install.ts b/x-pack/plugins/fleet/server/services/epm/packages/install.ts index 1a390df64d241..57c41f40cc1aa 100644 --- a/x-pack/plugins/fleet/server/services/epm/packages/install.ts +++ b/x-pack/plugins/fleet/server/services/epm/packages/install.ts @@ -605,6 +605,7 @@ export async function restartInstallation(options: { if (verificationResult) { savedObjectUpdate = { ...savedObjectUpdate, + verification_key_id: null, // unset any previous verification key id ...formatVerificationResultForSO(verificationResult), }; } From 17008e7a442799c5a03bb0b7a4a3fa5e558e04aa Mon Sep 17 00:00:00 2001 From: Mark Hopkin Date: Thu, 30 Jun 2022 17:10:28 +0100 Subject: [PATCH 12/18] self review --- .../integration_tests/validate_bundled_packages.test.ts | 2 +- x-pack/plugins/fleet/server/services/epm/packages/install.ts | 2 +- .../server/services/epm/packages/package_verification.ts | 2 +- x-pack/plugins/fleet/server/services/epm/registry/index.ts | 4 +++- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/x-pack/plugins/fleet/server/integration_tests/validate_bundled_packages.test.ts b/x-pack/plugins/fleet/server/integration_tests/validate_bundled_packages.test.ts index e59d5d0f5f109..9cd29809d562c 100644 --- a/x-pack/plugins/fleet/server/integration_tests/validate_bundled_packages.test.ts +++ b/x-pack/plugins/fleet/server/integration_tests/validate_bundled_packages.test.ts @@ -49,7 +49,7 @@ describe('validate bundled packages', () => { const packageArchive = await Registry.fetchArchiveBuffer({ pkgName: bundledPackage.name, pkgVersion: bundledPackage.version, - verify: true, + verify: false, }); return { registryPackage, packageArchive }; diff --git a/x-pack/plugins/fleet/server/services/epm/packages/install.ts b/x-pack/plugins/fleet/server/services/epm/packages/install.ts index 57c41f40cc1aa..88e6d88db18ba 100644 --- a/x-pack/plugins/fleet/server/services/epm/packages/install.ts +++ b/x-pack/plugins/fleet/server/services/epm/packages/install.ts @@ -440,7 +440,7 @@ async function installPackageByUpload({ telemetryEvent.currentVersion = installedPkg?.attributes.version || 'not_installed'; const installSource = 'upload'; - // as we do not verify uploaded packages, we must set the + // as we do not verify uploaded packages, we must invalidate the verification cache deleteVerificationResult(packageInfo); const paths = await unpackBufferToCache({ name: packageInfo.name, diff --git a/x-pack/plugins/fleet/server/services/epm/packages/package_verification.ts b/x-pack/plugins/fleet/server/services/epm/packages/package_verification.ts index fce47d99d4ab2..534dff83952bc 100644 --- a/x-pack/plugins/fleet/server/services/epm/packages/package_verification.ts +++ b/x-pack/plugins/fleet/server/services/epm/packages/package_verification.ts @@ -86,7 +86,7 @@ export async function verifyPackageArchiveSignature({ }); if (!pkgArchiveSignature) { - logger.warn(`Not performing package verification as package has no signature file`); + logger.warn(`Not performing package verification as package has no signature`); return result; } diff --git a/x-pack/plugins/fleet/server/services/epm/registry/index.ts b/x-pack/plugins/fleet/server/services/epm/registry/index.ts index d2d468a2afd2a..63fe8acc2efde 100644 --- a/x-pack/plugins/fleet/server/services/epm/registry/index.ts +++ b/x-pack/plugins/fleet/server/services/epm/registry/index.ts @@ -329,7 +329,9 @@ export async function getPackageArchiveSignatureOrUndefined({ const { signature_path: signaturePath } = await getInfo(pkgName, pkgVersion); if (!signaturePath) { - logger.warn(`Package ${pkgName}-${pkgVersion} does not have signature_file specified.`); + logger.debug( + `Package ${pkgName}-${pkgVersion} does not have a signature_path, verification will not be possible.` + ); return undefined; } From d755572a1e1e8a898dbc9c028c610da02176ccce Mon Sep 17 00:00:00 2001 From: Mark Hopkin Date: Thu, 30 Jun 2022 17:33:14 +0100 Subject: [PATCH 13/18] style: neaten types --- .../fleet/server/services/epm/archive/cache.ts | 3 +-- .../server/services/epm/packages/_install_package.ts | 12 +++++++++--- .../fleet/server/services/epm/packages/install.ts | 12 ++++++++---- .../services/epm/packages/package_verification.ts | 7 +------ .../fleet/server/services/epm/registry/index.ts | 2 +- x-pack/plugins/fleet/server/types/index.tsx | 9 +++++++++ 6 files changed, 29 insertions(+), 16 deletions(-) diff --git a/x-pack/plugins/fleet/server/services/epm/archive/cache.ts b/x-pack/plugins/fleet/server/services/epm/archive/cache.ts index 31778387d14e0..7b9aae17e68ab 100644 --- a/x-pack/plugins/fleet/server/services/epm/archive/cache.ts +++ b/x-pack/plugins/fleet/server/services/epm/archive/cache.ts @@ -5,8 +5,7 @@ * 2.0. */ -import type { ArchivePackage, RegistryPackage } from '../../../../common'; -import type { PackageVerificationResult } from '../packages/package_verification'; +import type { ArchivePackage, RegistryPackage, PackageVerificationResult } from '../../../types'; import { appContextService } from '../..'; import type { ArchiveEntry } from '.'; diff --git a/x-pack/plugins/fleet/server/services/epm/packages/_install_package.ts b/x-pack/plugins/fleet/server/services/epm/packages/_install_package.ts index faee321f89336..ec7e65c2b3875 100644 --- a/x-pack/plugins/fleet/server/services/epm/packages/_install_package.ts +++ b/x-pack/plugins/fleet/server/services/epm/packages/_install_package.ts @@ -19,9 +19,16 @@ import { PACKAGE_POLICY_SAVED_OBJECT_TYPE, SO_SEARCH_LIMIT, } from '../../../../common'; -import type { InstallablePackage, InstallSource, PackageAssetReference } from '../../../../common'; import { PACKAGES_SAVED_OBJECT_TYPE, FLEET_INSTALL_FORMAT_VERSION } from '../../../constants'; -import type { AssetReference, Installation, InstallType } from '../../../types'; +import type { + AssetReference, + Installation, + InstallType, + InstallablePackage, + InstallSource, + PackageAssetReference, + PackageVerificationResult, +} from '../../../types'; import { prepareToInstallTemplates } from '../elasticsearch/template/install'; import { removeLegacyTemplates } from '../elasticsearch/template/remove_legacy'; import { @@ -41,7 +48,6 @@ import { packagePolicyService } from '../..'; import { createInstallation, updateEsAssetReferences, restartInstallation } from './install'; import { withPackageSpan } from './utils'; -import type { PackageVerificationResult } from './package_verification'; // this is only exported for testing // use a leading underscore to indicate it's not the supported path diff --git a/x-pack/plugins/fleet/server/services/epm/packages/install.ts b/x-pack/plugins/fleet/server/services/epm/packages/install.ts index 88e6d88db18ba..c5e5f968c45c7 100644 --- a/x-pack/plugins/fleet/server/services/epm/packages/install.ts +++ b/x-pack/plugins/fleet/server/services/epm/packages/install.ts @@ -22,18 +22,23 @@ import pRetry from 'p-retry'; import { FLEET_INSTALL_FORMAT_VERSION } from '../../../constants/fleet_es_assets'; import { generateESIndexPatterns } from '../elasticsearch/template/template'; + import type { BulkInstallPackageInfo, EpmPackageInstallStatus, + EsAssetReference, InstallablePackage, + Installation, + InstallResult, InstallSource, -} from '../../../../common'; + InstallType, + KibanaAssetType, + PackageVerificationResult, +} from '../../../types'; import { AUTO_UPGRADE_POLICIES_PACKAGES } from '../../../../common'; import { IngestManagerError, PackageOutdatedError } from '../../../errors'; import { PACKAGES_SAVED_OBJECT_TYPE, MAX_TIME_COMPLETE_INSTALL } from '../../../constants'; -import type { KibanaAssetType } from '../../../types'; import { licenseService } from '../..'; -import type { Installation, EsAssetReference, InstallType, InstallResult } from '../../../types'; import { appContextService } from '../../app_context'; import * as Registry from '../registry'; import { @@ -48,7 +53,6 @@ import type { ArchiveAsset } from '../kibana/assets/install'; import type { PackageUpdateEvent } from '../../upgrade_sender'; import { sendTelemetryEvents, UpdateEventType } from '../../upgrade_sender'; -import type { PackageVerificationResult } from './package_verification'; import { formatVerificationResultForSO } from './package_verification'; import { getInstallation, getInstallationObject } from '.'; diff --git a/x-pack/plugins/fleet/server/services/epm/packages/package_verification.ts b/x-pack/plugins/fleet/server/services/epm/packages/package_verification.ts index 534dff83952bc..fae0cfa819259 100644 --- a/x-pack/plugins/fleet/server/services/epm/packages/package_verification.ts +++ b/x-pack/plugins/fleet/server/services/epm/packages/package_verification.ts @@ -10,7 +10,7 @@ import { readFile } from 'fs/promises'; import * as openpgp from 'openpgp'; import type { Logger } from '@kbn/logging'; -import type { PackageVerificationStatus } from '../../../../common'; +import type { PackageVerificationResult } from '../../../types'; import * as Registry from '../registry'; @@ -22,11 +22,6 @@ interface VerificationResult { keyId: string; } -export interface PackageVerificationResult { - verificationKeyId?: string; - verificationStatus: PackageVerificationStatus; -} - let cachedKey: openpgp.Key | undefined | null = null; export async function getGpgKeyOrUndefined(): Promise { diff --git a/x-pack/plugins/fleet/server/services/epm/registry/index.ts b/x-pack/plugins/fleet/server/services/epm/registry/index.ts index 63fe8acc2efde..3bc9bc7dfbf1c 100644 --- a/x-pack/plugins/fleet/server/services/epm/registry/index.ts +++ b/x-pack/plugins/fleet/server/services/epm/registry/index.ts @@ -23,6 +23,7 @@ import type { RegistryPackage, RegistrySearchResults, GetCategoriesRequest, + PackageVerificationResult, } from '../../../types'; import { getArchiveFilelist, @@ -45,7 +46,6 @@ import { getBundledPackageByName } from '../packages/bundled_packages'; import { withPackageSpan } from '../packages/utils'; -import type { PackageVerificationResult } from '../packages/package_verification'; import { verifyPackageArchiveSignature } from '../packages/package_verification'; import { fetchUrl, getResponse, getResponseStream } from './requests'; diff --git a/x-pack/plugins/fleet/server/types/index.tsx b/x-pack/plugins/fleet/server/types/index.tsx index 1d83d4011115c..2ddf79ff39ef4 100644 --- a/x-pack/plugins/fleet/server/types/index.tsx +++ b/x-pack/plugins/fleet/server/types/index.tsx @@ -77,6 +77,9 @@ export type { DownloadSourceBase, DownloadSource, DownloadSourceAttributes, + PackageVerificationStatus, + BulkInstallPackageInfo, + PackageAssetReference, } from '../../common'; export { ElasticsearchAssetType, @@ -96,6 +99,12 @@ export interface BulkActionResult { error?: Error; } +import type { PackageVerificationStatus } from '../../common'; +export interface PackageVerificationResult { + verificationKeyId?: string; + verificationStatus: PackageVerificationStatus; +} + export * from './models'; export * from './rest_spec'; export * from './extensions'; From a0711bf83ec4f7f6bba9967b6cb188a1edab6e1a Mon Sep 17 00:00:00 2001 From: Mark Hopkin Date: Thu, 30 Jun 2022 18:18:47 +0100 Subject: [PATCH 14/18] pr: Return 400 on verification error --- x-pack/plugins/fleet/server/errors/handlers.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/fleet/server/errors/handlers.ts b/x-pack/plugins/fleet/server/errors/handlers.ts index 3d92432658751..1eb0aed4ad690 100644 --- a/x-pack/plugins/fleet/server/errors/handlers.ts +++ b/x-pack/plugins/fleet/server/errors/handlers.ts @@ -62,7 +62,7 @@ const getHTTPResponseCode = (error: IngestManagerError): number => { return 415; // Unsupported Media Type } if (error instanceof PackageFailedVerificationError) { - return 422; // Unprocessable Entity + return 400; // Bad Request } if (error instanceof ConcurrentInstallOperationError) { return 409; // Conflict From 3dd6d3b5938a8773f27fdb1d50f3c85c6bccedb4 Mon Sep 17 00:00:00 2001 From: Mark Hopkin Date: Thu, 30 Jun 2022 18:32:33 +0100 Subject: [PATCH 15/18] fix tests --- .../fleet/server/services/epm/package_service.test.ts | 2 +- .../fleet/server/services/epm/packages/install.test.ts | 5 ++++- .../fleet_api_integration/apis/epm/install_remove_assets.ts | 1 + 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/fleet/server/services/epm/package_service.test.ts b/x-pack/plugins/fleet/server/services/epm/package_service.test.ts index 782af2860d2e3..1dc4039a7c2a7 100644 --- a/x-pack/plugins/fleet/server/services/epm/package_service.test.ts +++ b/x-pack/plugins/fleet/server/services/epm/package_service.test.ts @@ -102,7 +102,7 @@ function getTest( method: mocks.packageClient.getRegistryPackage.bind(mocks.packageClient), args: ['package name', '8.0.0'], spy: jest.spyOn(epmRegistry, 'getRegistryPackage'), - spyArgs: ['package name', '8.0.0'], + spyArgs: ['package name', '8.0.0', undefined], spyResponse: { packageInfo: { name: 'getRegistryPackage test' }, paths: ['/some/test/path'], diff --git a/x-pack/plugins/fleet/server/services/epm/packages/install.test.ts b/x-pack/plugins/fleet/server/services/epm/packages/install.test.ts index 76cf09b0cad06..7177e8b4aa085 100644 --- a/x-pack/plugins/fleet/server/services/epm/packages/install.test.ts +++ b/x-pack/plugins/fleet/server/services/epm/packages/install.test.ts @@ -59,6 +59,7 @@ jest.mock('../archive', () => { ), unpackBufferToCache: jest.fn(), setPackageInfo: jest.fn(), + deleteVerificationResult: jest.fn(), }; }); @@ -213,7 +214,7 @@ describe('install', () => { }, ]); - await installPackage({ + const response = await installPackage({ spaceId: DEFAULT_SPACE_ID, installSource: 'registry', pkgkey: 'test_package-1.0.0', @@ -221,6 +222,8 @@ describe('install', () => { esClient: {} as ElasticsearchClient, }); + expect(response.error).toBeUndefined(); + expect(install._installPackage).toHaveBeenCalledWith( expect.objectContaining({ installSource: 'upload' }) ); diff --git a/x-pack/test/fleet_api_integration/apis/epm/install_remove_assets.ts b/x-pack/test/fleet_api_integration/apis/epm/install_remove_assets.ts index d142dc6fd3515..0a135f0d645a3 100644 --- a/x-pack/test/fleet_api_integration/apis/epm/install_remove_assets.ts +++ b/x-pack/test/fleet_api_integration/apis/epm/install_remove_assets.ts @@ -767,6 +767,7 @@ const expectAssetsInstalled = ({ install_started_at: res.attributes.install_started_at, install_source: 'registry', install_format_schema_version: FLEET_INSTALL_FORMAT_VERSION, + verification_status: 'unknown', }); }); }; From f5e4d725945b5a0646440655e326d2996f4d4907 Mon Sep 17 00:00:00 2001 From: Mark Hopkin Date: Mon, 4 Jul 2022 11:55:36 +0100 Subject: [PATCH 16/18] more test fixes --- .../plugins/fleet/server/services/epm/packages/install.test.ts | 1 + x-pack/test/fleet_api_integration/apis/epm/update_assets.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/x-pack/plugins/fleet/server/services/epm/packages/install.test.ts b/x-pack/plugins/fleet/server/services/epm/packages/install.test.ts index 7177e8b4aa085..ac53717bb2b1a 100644 --- a/x-pack/plugins/fleet/server/services/epm/packages/install.test.ts +++ b/x-pack/plugins/fleet/server/services/epm/packages/install.test.ts @@ -206,6 +206,7 @@ describe('install', () => { }); it('should install from bundled package if one exists', async () => { + (install._installPackage as jest.Mock).mockResolvedValue({}); mockGetBundledPackages.mockResolvedValue([ { name: 'test_package', diff --git a/x-pack/test/fleet_api_integration/apis/epm/update_assets.ts b/x-pack/test/fleet_api_integration/apis/epm/update_assets.ts index f31c4cd44b80d..c38e79cc760a2 100644 --- a/x-pack/test/fleet_api_integration/apis/epm/update_assets.ts +++ b/x-pack/test/fleet_api_integration/apis/epm/update_assets.ts @@ -513,6 +513,7 @@ export default function (providerContext: FtrProviderContext) { install_started_at: res.attributes.install_started_at, install_source: 'registry', install_format_schema_version: FLEET_INSTALL_FORMAT_VERSION, + verification_status: 'unknown', }); }); }); From 36a17dec3d366d865f2f2cbca9bed2b464860976 Mon Sep 17 00:00:00 2001 From: Mark Hopkin Date: Thu, 7 Jul 2022 11:19:16 +0100 Subject: [PATCH 17/18] PR feedback --- .../server/services/epm/packages/package_verification.ts | 4 +++- .../plugins/fleet/server/services/epm/registry/index.ts | 8 ++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/x-pack/plugins/fleet/server/services/epm/packages/package_verification.ts b/x-pack/plugins/fleet/server/services/epm/packages/package_verification.ts index fae0cfa819259..0e39cefaf1169 100644 --- a/x-pack/plugins/fleet/server/services/epm/packages/package_verification.ts +++ b/x-pack/plugins/fleet/server/services/epm/packages/package_verification.ts @@ -81,7 +81,9 @@ export async function verifyPackageArchiveSignature({ }); if (!pkgArchiveSignature) { - logger.warn(`Not performing package verification as package has no signature`); + logger.warn( + `Package ${pkgName}-${pkgVersion} has no corresponding signature. Skipping verification.` + ); return result; } diff --git a/x-pack/plugins/fleet/server/services/epm/registry/index.ts b/x-pack/plugins/fleet/server/services/epm/registry/index.ts index 3bc9bc7dfbf1c..6d0ab10e41d98 100644 --- a/x-pack/plugins/fleet/server/services/epm/registry/index.ts +++ b/x-pack/plugins/fleet/server/services/epm/registry/index.ts @@ -252,7 +252,7 @@ export async function getRegistryPackage( fetchArchiveBuffer({ pkgName: name, pkgVersion: version, - verify: verifyPackage, + shouldVerify: verifyPackage, ignoreUnverified: options?.ignoreUnverified, }) ); @@ -285,12 +285,12 @@ function ensureContentType(archivePath: string) { export async function fetchArchiveBuffer({ pkgName, pkgVersion, - verify, + shouldVerify, ignoreUnverified = false, }: { pkgName: string; pkgVersion: string; - verify: boolean; + shouldVerify: boolean; ignoreUnverified?: boolean; }): Promise<{ archiveBuffer: Buffer; @@ -301,7 +301,7 @@ export async function fetchArchiveBuffer({ const { download: archivePath } = await getInfo(pkgName, pkgVersion); const archiveUrl = `${getRegistryUrl()}${archivePath}`; const archiveBuffer = await getResponseStream(archiveUrl).then(streamToBuffer); - if (verify) { + if (shouldVerify) { const verificationResult = await verifyPackageArchiveSignature({ pkgName, pkgVersion, From a87848dcc0480359760216db792e73faed305820 Mon Sep 17 00:00:00 2001 From: Mark Hopkin Date: Thu, 7 Jul 2022 12:09:46 +0100 Subject: [PATCH 18/18] fix types --- .../server/integration_tests/validate_bundled_packages.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/fleet/server/integration_tests/validate_bundled_packages.test.ts b/x-pack/plugins/fleet/server/integration_tests/validate_bundled_packages.test.ts index 9cd29809d562c..6b5f3efeae538 100644 --- a/x-pack/plugins/fleet/server/integration_tests/validate_bundled_packages.test.ts +++ b/x-pack/plugins/fleet/server/integration_tests/validate_bundled_packages.test.ts @@ -49,7 +49,7 @@ describe('validate bundled packages', () => { const packageArchive = await Registry.fetchArchiveBuffer({ pkgName: bundledPackage.name, pkgVersion: bundledPackage.version, - verify: false, + shouldVerify: false, }); return { registryPackage, packageArchive };