From 0ec427aedb336bb36c3d815a70704218ae108904 Mon Sep 17 00:00:00 2001 From: Nicolas Chaulet Date: Tue, 30 Jun 2020 14:15:47 -0400 Subject: [PATCH] [Ingest Manager] Validate agent version <= Kibana version (#70339) --- .../server/services/agents/enroll.ts | 8 ++++ .../api_integration/apis/fleet/agent_flow.ts | 8 +++- .../apis/fleet/agents/enroll.ts | 42 +++++++++++++++++-- 3 files changed, 53 insertions(+), 5 deletions(-) diff --git a/x-pack/plugins/ingest_manager/server/services/agents/enroll.ts b/x-pack/plugins/ingest_manager/server/services/agents/enroll.ts index 2487035a338aa..bf15815e6ae41 100644 --- a/x-pack/plugins/ingest_manager/server/services/agents/enroll.ts +++ b/x-pack/plugins/ingest_manager/server/services/agents/enroll.ts @@ -5,11 +5,13 @@ */ import Boom from 'boom'; +import semver from 'semver'; import { SavedObjectsClientContract } from 'src/core/server'; import { AgentType, Agent, AgentSOAttributes } from '../../types'; import { savedObjectToAgent } from './saved_objects'; import { AGENT_SAVED_OBJECT_TYPE } from '../../constants'; import * as APIKeyService from '../api_keys'; +import { appContextService } from '../app_context'; export async function enroll( soClient: SavedObjectsClientContract, @@ -18,6 +20,12 @@ export async function enroll( metadata?: { local: any; userProvided: any }, sharedId?: string ): Promise { + const kibanaVersion = appContextService.getKibanaVersion(); + const version: string | undefined = metadata?.local?.elastic?.agent?.version; + if (!version || semver.compare(version, kibanaVersion) === 1) { + throw Boom.badRequest('Agent version is not compatible with kibana version'); + } + const existingAgent = sharedId ? await getAgentBySharedId(soClient, sharedId) : null; if (existingAgent && existingAgent.active === true) { diff --git a/x-pack/test/api_integration/apis/fleet/agent_flow.ts b/x-pack/test/api_integration/apis/fleet/agent_flow.ts index e9e9f0e0208e4..400db995576ae 100644 --- a/x-pack/test/api_integration/apis/fleet/agent_flow.ts +++ b/x-pack/test/api_integration/apis/fleet/agent_flow.ts @@ -13,6 +13,8 @@ export default function (providerContext: FtrProviderContext) { const { getService } = providerContext; const esArchiver = getService('esArchiver'); const supertest = getService('supertest'); + const kibanaServer = getService('kibanaServer'); + const supertestWithoutAuth = getSupertestWithoutAuth(providerContext); const esClient = getService('es'); @@ -26,6 +28,8 @@ export default function (providerContext: FtrProviderContext) { }); it('should work', async () => { + const kibanaVersionAccessor = kibanaServer.version; + const kibanaVersion = await kibanaVersionAccessor.get(); // 1. Get enrollment token const { body: enrollmentApiKeysResponse } = await supertest .get(`/api/ingest_manager/fleet/enrollment-api-keys`) @@ -48,7 +52,9 @@ export default function (providerContext: FtrProviderContext) { .send({ type: 'PERMANENT', metadata: { - local: {}, + local: { + elastic: { agent: { version: kibanaVersion } }, + }, user_provided: {}, }, }) diff --git a/x-pack/test/api_integration/apis/fleet/agents/enroll.ts b/x-pack/test/api_integration/apis/fleet/agents/enroll.ts index ca9a0d57a3ea6..b4d23a2392320 100644 --- a/x-pack/test/api_integration/apis/fleet/agents/enroll.ts +++ b/x-pack/test/api_integration/apis/fleet/agents/enroll.ts @@ -15,9 +15,11 @@ export default function (providerContext: FtrProviderContext) { const esArchiver = getService('esArchiver'); const esClient = getService('es'); + const kibanaServer = getService('kibanaServer'); const supertest = getSupertestWithoutAuth(providerContext); let apiKey: { id: string; api_key: string }; + let kibanaVersion: string; describe('fleet_agents_enroll', () => { before(async () => { @@ -45,6 +47,8 @@ export default function (providerContext: FtrProviderContext) { doc: enrollmentApiKeyDoc, }, }); + const kibanaVersionAccessor = kibanaServer.version; + kibanaVersion = await kibanaVersionAccessor.get(); }); setupIngest(providerContext); after(async () => { @@ -59,7 +63,9 @@ export default function (providerContext: FtrProviderContext) { .send({ type: 'PERMANENT', metadata: { - local: {}, + local: { + elastic: { agent: { version: kibanaVersion } }, + }, user_provided: {}, }, }) @@ -78,7 +84,9 @@ export default function (providerContext: FtrProviderContext) { shared_id: 'agent2_filebeat', type: 'PERMANENT', metadata: { - local: {}, + local: { + elastic: { agent: { version: kibanaVersion } }, + }, user_provided: {}, }, }) @@ -86,6 +94,28 @@ export default function (providerContext: FtrProviderContext) { expect(apiResponse.message).to.match(/Impossible to enroll an already active agent/); }); + it('should not allow to enroll an agent with a version > kibana', async () => { + const { body: apiResponse } = await supertest + .post(`/api/ingest_manager/fleet/agents/enroll`) + .set('kbn-xsrf', 'xxx') + .set( + 'authorization', + `ApiKey ${Buffer.from(`${apiKey.id}:${apiKey.api_key}`).toString('base64')}` + ) + .send({ + shared_id: 'agent2_filebeat', + type: 'PERMANENT', + metadata: { + local: { + elastic: { agent: { version: '999.0.0' } }, + }, + user_provided: {}, + }, + }) + .expect(400); + expect(apiResponse.message).to.match(/Agent version is not compatible with kibana/); + }); + it('should allow to enroll an agent with a valid enrollment token', async () => { const { body: apiResponse } = await supertest .post(`/api/ingest_manager/fleet/agents/enroll`) @@ -97,7 +127,9 @@ export default function (providerContext: FtrProviderContext) { .send({ type: 'PERMANENT', metadata: { - local: {}, + local: { + elastic: { agent: { version: kibanaVersion } }, + }, user_provided: {}, }, }) @@ -117,7 +149,9 @@ export default function (providerContext: FtrProviderContext) { .send({ type: 'PERMANENT', metadata: { - local: {}, + local: { + elastic: { agent: { version: kibanaVersion } }, + }, user_provided: {}, }, })