Skip to content

Commit

Permalink
[Fleet][Tests] Package install signature verification API tests (#136947
Browse files Browse the repository at this point in the history
) (#138027)

* add valid signature test package

* add test signatures and readme for signature generation

* mount zip packages as part of tests

* amend README

* Verified package test working

* Rename valid to verified

* Add test for unverified content

* add test for package verified with wrong key

* Check error types in 400 response

* Check saved object keys as part of tests

* Remove wrong_ keys

* use release docker image

* update package path for v2 registry

* force install endpoint package

* fix package policy upgrade on setup test

* formatting

* move back to production registry

* Update all registry configs to use new package directory

* use specific docker image not tag

* fix agent policy tests

* Get latest experimental endpoint version

* skip impossible fleet test

* update synthetics to use same registry image as fleet

* fix telemetry tests

* remove experimental flag from test config

* add force install confirm to synthetics tests

* add origin to expected policy data

* add test subj to force install modal

* Install latest fleet_server package not fixed version

* install latest system pkg

* fix types

* fix deprecated API calls

(cherry picked from commit 9f8a2c6)

# Conflicts:
#	x-pack/test/fleet_api_integration/config.ts
#	x-pack/test/functional_synthetics/config.js
  • Loading branch information
hop-dev authored Aug 3, 2022
1 parent fa02234 commit 7679ff4
Show file tree
Hide file tree
Showing 49 changed files with 1,246 additions and 76 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,2 @@
package_paths:
- /packages/production
- /packages/snapshot
- /packages/test-packages
- /packages/package-storage
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ export const ConfirmForceInstallModal: React.FC<{
/>
}
buttonColor="danger"
data-test-subj="confirmForceInstallModal"
>
<EuiCallOut
title={title}
Expand Down
52 changes: 36 additions & 16 deletions x-pack/test/fleet_api_integration/apis/agent_policy/agent_policy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,20 @@ export default function (providerContext: FtrProviderContext) {
const supertest = getService('supertest');
const esArchiver = getService('esArchiver');
const kibanaServer = getService('kibanaServer');

describe('fleet_agent_policies', () => {
skipIfNoDockerRegistry(providerContext);
describe('POST /api/fleet/agent_policies', () => {
let systemPkgVersion: string;
before(async () => {
await esArchiver.load('x-pack/test/functional/es_archives/fleet/empty_fleet_server');
await esArchiver.load('x-pack/test/functional/es_archives/empty_kibana');
});
setupFleetAndAgents(providerContext);
const packagePoliciesToDeleteIds: string[] = [];
let packagePoliciesToDeleteIds: string[] = [];
after(async () => {
if (systemPkgVersion) {
await supertest.delete(`/api/fleet/epm/packages/system-${systemPkgVersion}`);
}
if (packagePoliciesToDeleteIds.length > 0) {
await kibanaServer.savedObjects.bulkDelete({
objects: packagePoliciesToDeleteIds.map((id) => ({
Expand Down Expand Up @@ -173,20 +176,36 @@ export default function (providerContext: FtrProviderContext) {

it('should allow to create policy with the system integration policy and increment correctly the name if there is more than 10 package policy', async () => {
// load a bunch of fake system integration policy
for (let i = 0; i < 10; i++) {
await kibanaServer.savedObjects.create({
id: `package-policy-test-${i}`,
type: PACKAGE_POLICY_SAVED_OBJECT_TYPE,
overwrite: true,
attributes: {
name: `system-${i + 1}`,
package: {
name: 'system',
const policyIds = new Array(10).fill(null).map((_, i) => `package-policy-test-${i}`);
packagePoliciesToDeleteIds = packagePoliciesToDeleteIds.concat(policyIds);
const getPkRes = await supertest
.get(`/api/fleet/epm/packages/system`)
.set('kbn-xsrf', 'xxxx')
.expect(200);
systemPkgVersion = getPkRes.body.item.version;
// we must first force install the system package to override package verification error on policy create
const installPromise = supertest
.post(`/api/fleet/epm/packages/system-${systemPkgVersion}`)
.set('kbn-xsrf', 'xxxx')
.send({ force: true })
.expect(200);

await Promise.all([
installPromise,
...policyIds.map((policyId, i) =>
kibanaServer.savedObjects.create({
id: policyId,
type: PACKAGE_POLICY_SAVED_OBJECT_TYPE,
overwrite: true,
attributes: {
name: `system-${i + 1}`,
package: {
name: 'system',
},
},
},
});
packagePoliciesToDeleteIds.push(`package-policy-test-${i}`);
}
})
),
]);

// first one succeeds
const res = await supertest
Expand Down Expand Up @@ -575,7 +594,8 @@ export default function (providerContext: FtrProviderContext) {
);
});

it('should return a 200 if updating monitoring_enabled on a policy', async () => {
// Skipped as cannot force install the system and agent integrations as part of policy creation https://github.com/elastic/kibana/issues/137450
it.skip('should return a 200 if updating monitoring_enabled on a policy', async () => {
const fetchPackageList = async () => {
const response = await supertest
.get('/api/fleet/epm/packages')
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
/*
* 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 type { Client } from '@elastic/elasticsearch';
import expect from '@kbn/expect';
import { Installation } from '@kbn/fleet-plugin/server/types';
import { FtrProviderContext } from '../../../api_integration/ftr_provider_context';
import { skipIfNoDockerRegistry } from '../../helpers';
import { setupFleetAndAgents } from '../agents/services';

const TEST_KEY_ID = 'd2a182a7b0e00c14';
export default function (providerContext: FtrProviderContext) {
const { getService } = providerContext;
const es: Client = getService('es');
const supertest = getService('supertest');
const dockerServers = getService('dockerServers');
const server = dockerServers.get('registry');

const uninstallPackage = async (pkg: string, version: string) => {
await supertest.delete(`/api/fleet/epm/packages/${pkg}/${version}`).set('kbn-xsrf', 'xxxx');
};
const installPackage = (pkg: string, version: string, opts?: { force?: boolean }) => {
return supertest
.post(`/api/fleet/epm/packages/${pkg}/${version}`)
.set('kbn-xsrf', 'xxxx')
.send({ force: !!opts?.force });
};

const getInstallationSavedObject = async (pkg: string): Promise<Installation | undefined> => {
const res: { _source?: { 'epm-packages': Installation } } = await es.transport.request({
method: 'GET',
path: `/.kibana/_doc/epm-packages:${pkg}`,
});

return res?._source?.['epm-packages'] as Installation;
};

describe('Installs verified and unverified packages', async () => {
skipIfNoDockerRegistry(providerContext);
setupFleetAndAgents(providerContext);

describe('verified package', async () => {
after(async () => {
if (!server.enabled) return;
await uninstallPackage('verified', '1.0.0');
});
it('should install a package with a valid signature', async () => {
await installPackage('verified', '1.0.0').expect(200);
const installationSO = await getInstallationSavedObject('verified');
expect(installationSO?.verification_status).equal('verified');
expect(installationSO?.verification_key_id).equal(TEST_KEY_ID);
});
});
describe('unverified packages', async () => {
describe('unverified package content', async () => {
after(async () => {
if (!server.enabled) return;
await uninstallPackage('unverified_content', '1.0.0');
});
it('should return 400 for valid signature but incorrect content', async () => {
const res = await installPackage('unverified_content', '1.0.0');

expect(res.status).equal(400);
expect(res.body.attributes).eql({
type: 'verification_failed',
});
});
it('should return 200 for valid signature but incorrect content force install', async () => {
await installPackage('unverified_content', '1.0.0', { force: true }).expect(200);
const installationSO = await getInstallationSavedObject('unverified_content');
expect(installationSO?.verification_status).equal('unverified');
expect(installationSO?.verification_key_id).equal(TEST_KEY_ID);
});
});
describe('package verified with wrong key', async () => {
after(async () => {
if (!server.enabled) return;
await uninstallPackage('wrong_key', '1.0.0');
});
it('should return 400 for valid signature but incorrect key', async () => {
const res = await installPackage('wrong_key', '1.0.0');
expect(res.status).equal(400);
expect(res.body.attributes).eql({
type: 'verification_failed',
});
});
it('should return 200 for valid signature but incorrect key force install', async () => {
await installPackage('wrong_key', '1.0.0', { force: true }).expect(200);
const installationSO = await getInstallationSavedObject('wrong_key');
expect(installationSO?.verification_status).equal('unverified');
expect(installationSO?.verification_key_id).equal(TEST_KEY_ID);
});
});
});
});
}
41 changes: 22 additions & 19 deletions x-pack/test/fleet_api_integration/apis/epm/setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,49 +74,52 @@ export default function (providerContext: FtrProviderContext) {
});

it('should upgrade package policy on setup if keep policies up to date set to true', async () => {
const oldVersion = '1.11.0';
const oldVersion = '0.1.0';
const latestVersion = '0.3.0';
const policyName = 'policy-1';
// first install old version of package
await supertest
.post(`/api/fleet/epm/packages/system/${oldVersion}`)
.post(`/api/fleet/epm/packages/multiple_versions/${oldVersion}`)
.set('kbn-xsrf', 'xxxx')
.send({ force: true })
.expect(200);

// now set the package to keep policies up to date
await supertest
.put(`/api/fleet/epm/packages/system/${oldVersion}`)
.put(`/api/fleet/epm/packages/multiple_versions/${oldVersion}`)
.set('kbn-xsrf', 'xxxx')
.send({ keepPoliciesUpToDate: true })
.expect(200);

// create a package policy with the old package version
await supertest
.post('/api/fleet/package_policies')
.set('kbn-xsrf', 'xxxx')
.send({
name: 'system-1',
name: policyName,
namespace: 'default',
policy_id: agentPolicyId,
package: { name: 'system', version: oldVersion },
package: { name: 'multiple_versions', version: oldVersion },
inputs: [],
force: true,
})
.expect(200);

let { body } = await supertest
.get(`/api/fleet/epm/packages/system/${oldVersion}`)
.expect(200);
const latestVersion = body.item.latestVersion;
log.info(`System package latest version: ${latestVersion}`);
// make sure we're actually doing an upgrade
expect(latestVersion).not.eql(oldVersion);

({ body } = await supertest
.post(`/api/fleet/epm/packages/system/${latestVersion}`)
// install the most recent version of the package
await supertest
.post(`/api/fleet/epm/packages/multiple_versions/${latestVersion}`)
.set('kbn-xsrf', 'xxxx')
.expect(200));
.expect(200);

await supertest.post(`/api/fleet/setup`).set('kbn-xsrf', 'xxxx').expect(200);

({ body } = await supertest
// now check the package policy has been upgraded to the latest version
const { body } = await supertest
.get('/api/fleet/package_policies')
.set('kbn-xsrf', 'xxxx')
.expect(200));
expect(body.items.find((pkg: any) => pkg.name === 'system-1').package.version).to.equal(
.expect(200);

expect(body.items.find((pkg: any) => pkg.name === policyName).package.version).to.equal(
latestVersion
);
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
package_paths:
- /packages/production
# TODO remove temp
- /packages/snapshot
- /packages/package-storage
- /packages/test-packages
- /packages/signed-test-packages
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# Package verification fixtures


## Signatures folder
This directory contains a public private key pair to be used for testing package verification. These keys are purely for testing and do not contain or sign any sensitive information. Here is the key information:

```
pub rsa3072 2022-07-21 [SC]
EA69DC1F612FABF267850741D2A182A7B0E00C14
uid [ultimate] Fleet Test (Fleet Integration Test Key) <[email protected]>
```

The passphrase of the private key is 'test'

### How were the keys generated?

*Note: the key ID will be different.*
```
gpg --full-generate-key
# Kind: RSA
# Keysize: 3072
# Valid for: 0 (does not expire)
# Real name: Fleet Test
# Email address: [email protected]
# Comment: Fleet Integration Test Key
# Passphrase: test
gpg --armor --export EA69DC1F612FABF267850741D2A182A7B0E00C14 > fleet_test_key_public.asc
gpg --armor --export-secret-keys EA69DC1F612FABF267850741D2A182A7B0E00C14 > fleet_test_key_private.asc
```

After generating the keys, you may want to delete them from your local keystore:
```
gpg --delete-secret-keys EA69DC1F612FABF267850741D2A182A7B0E00C14
gpg --delete-keys EA69DC1F612FABF267850741D2A182A7B0E00C14
```
## Packages folder

## How were the packages generated?

### verified-1.0.0
The valid package was generated with the following commands:
```
export ELASTIC_PACKAGE_SIGNER_PRIVATE_KEYFILE=../../../signatures/fleet_test_key_private.asc
export ELASTIC_PACKAGE_SIGNER_PASSPHRASE=test
cd packages/src/verified-1.0.0
elastic-package build --zip --sign -v
# if successful then the last log line will contain:
# Signature file written: /<path to you kibana>/kibana/build/packages/verified-1.0.0.zip.sig
# Package built: /<path to you kibana>/kibana/build/packages/verified-1.0.0.zip
cp /<path to you kibana>/kibana/build/packages/verified-1.0.0.zip ../../zips/
cp /<path to you kibana>/kibana/build/packages/verified-1.0.0.zip.sig ../../zips/
```

### unverified_content-1.0.0
This package has a valid signature but for different content. Same process as verified-1.0.0, however it has the incorrect signature, in this case I use the verified signature:
```
# Same buld steps as above
cp /<path to you kibana>/kibana/build/packages/unverified_content-1.0.0.zip ../../zips/
# now copy the incorrect signature
cp ../../zips/verified-1.0.0.zip.sig ../../zips/unverified_content-1.0.0.zip.sig
```
### wrong_key-1.0.0
This package is signed correctly but not using the key that kibana uses. Same process as verified-1.0.0, however I generated a different key pair (See 'How were the keys generated?'), and specified it for the ELASTIC_PACKAGE_SIGNER_PRIVATE_KEYFILE 'elastic-package' argument
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
- version: "1.0.0"
changes:
- description: This is a test
type: enhancement
link: fakelink
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
paths:
{{#each paths}}
- {{this}}
{{/each}}

data_stream:
dataset: {{data_stream.dataset}}

{{custom}}
Loading

0 comments on commit 7679ff4

Please sign in to comment.