Skip to content

Commit

Permalink
[Fleet] Use fleet server indices for enrollment keys and to list agen…
Browse files Browse the repository at this point in the history
…ts with a feature flag (#86179) (#88924)
  • Loading branch information
nchaulet authored Jan 21, 2021
1 parent fc687b7 commit b35509c
Show file tree
Hide file tree
Showing 71 changed files with 1,528 additions and 406 deletions.
2 changes: 2 additions & 0 deletions x-pack/plugins/fleet/common/constants/agent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,5 @@ export const AGENT_UPDATE_ACTIONS_INTERVAL_MS = 5000;

export const AGENT_POLICY_ROLLOUT_RATE_LIMIT_INTERVAL_MS = 1000;
export const AGENT_POLICY_ROLLOUT_RATE_LIMIT_REQUEST_PER_INTERVAL = 5;

export const AGENTS_INDEX = '.fleet-agents';
2 changes: 1 addition & 1 deletion x-pack/plugins/fleet/common/constants/agent_policy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import { defaultPackages } from './epm';
import { AgentPolicy } from '../types';
export const AGENT_POLICY_SAVED_OBJECT_TYPE = 'ingest-agent-policies';

export const AGENT_POLICY_INDEX = '.fleet-policies';
export const agentPolicyStatuses = {
Active: 'active',
Inactive: 'inactive',
Expand Down
2 changes: 2 additions & 0 deletions x-pack/plugins/fleet/common/constants/enrollment_api_key.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,5 @@
*/

export const ENROLLMENT_API_KEYS_SAVED_OBJECT_TYPE = 'fleet-enrollment-api-keys';

export const ENROLLMENT_API_KEYS_INDEX = '.fleet-enrollment-api-keys';
4 changes: 2 additions & 2 deletions x-pack/plugins/fleet/common/services/agent_status.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export function getAgentStatus(agent: Agent, now: number = Date.now()): AgentSta
}

export function buildKueryForEnrollingAgents() {
return `not ${AGENT_SAVED_OBJECT_TYPE}.last_checkin:*`;
return `not (${AGENT_SAVED_OBJECT_TYPE}.last_checkin:*)`;
}

export function buildKueryForUnenrollingAgents() {
Expand All @@ -53,7 +53,7 @@ export function buildKueryForOnlineAgents() {
}

export function buildKueryForErrorAgents() {
return `( ${AGENT_SAVED_OBJECT_TYPE}.last_checkin_status:error or ${AGENT_SAVED_OBJECT_TYPE}.last_checkin_status:degraded )`;
return `${AGENT_SAVED_OBJECT_TYPE}.last_checkin_status:error or ${AGENT_SAVED_OBJECT_TYPE}.last_checkin_status:degraded`;
}

export function buildKueryForOfflineAgents() {
Expand Down
1 change: 1 addition & 0 deletions x-pack/plugins/fleet/common/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export interface FleetConfigType {
registryUrl?: string;
registryProxyUrl?: string;
agents: {
fleetServerEnabled: boolean;
enabled: boolean;
tlsCheckDisabled: boolean;
pollingRequestTimeout: number;
Expand Down
34 changes: 34 additions & 0 deletions x-pack/plugins/fleet/common/types/models/agent_policy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,3 +80,37 @@ export interface FullAgentPolicyKibanaConfig {
protocol: string;
path?: string;
}

// Generated from Fleet Server schema.json

/**
* A policy that an Elastic Agent is attached to
*/
export interface FleetServerPolicy {
/**
* Date/time the policy revision was created
*/
'@timestamp'?: string;
/**
* The ID of the policy
*/
policy_id: string;
/**
* The revision index of the policy
*/
revision_idx: number;
/**
* The coordinator index of the policy
*/
coordinator_idx: number;
/**
* The opaque payload.
*/
data: {
[k: string]: unknown;
};
/**
* True when this policy is the default policy to start Fleet Server
*/
default_fleet_server: boolean;
}
28 changes: 28 additions & 0 deletions x-pack/plugins/fleet/common/types/models/enrollment_api_key.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,31 @@ export interface EnrollmentAPIKey {
}

export type EnrollmentAPIKeySOAttributes = Omit<EnrollmentAPIKey, 'id'>;

// Generated

/**
* An Elastic Agent enrollment API key
*/
export interface FleetServerEnrollmentAPIKey {
/**
* True when the key is active
*/
active?: boolean;
/**
* The unique identifier for the enrollment key, currently xid
*/
api_key_id: string;
/**
* Api key
*/
api_key: string;
/**
* Enrollment key name
*/
name?: string;
policy_id?: string;
expire_at?: string;
created_at?: string;
updated_at?: string;
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export const createConfigurationMock = (): FleetConfigType => {
registryProxyUrl: '',
agents: {
enabled: true,
fleetServerEnabled: false,
tlsCheckDisabled: true,
pollingRequestTimeout: 1000,
maxConcurrentConnections: 100,
Expand Down
12 changes: 8 additions & 4 deletions x-pack/plugins/fleet/server/collectors/agent_collectors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/

import { SavedObjectsClient } from 'kibana/server';
import { ElasticsearchClient, SavedObjectsClient } from 'kibana/server';
import * as AgentService from '../services/agents';
export interface AgentUsage {
total: number;
Expand All @@ -13,9 +13,12 @@ export interface AgentUsage {
offline: number;
}

export const getAgentUsage = async (soClient?: SavedObjectsClient): Promise<AgentUsage> => {
export const getAgentUsage = async (
soClient?: SavedObjectsClient,
esClient?: ElasticsearchClient
): Promise<AgentUsage> => {
// TODO: unsure if this case is possible at all.
if (!soClient) {
if (!soClient || !esClient) {
return {
total: 0,
online: 0,
Expand All @@ -24,7 +27,8 @@ export const getAgentUsage = async (soClient?: SavedObjectsClient): Promise<Agen
};
}
const { total, online, error, offline } = await AgentService.getAgentStatusForAgentPolicy(
soClient
soClient,
esClient
);
return {
total,
Expand Down
9 changes: 6 additions & 3 deletions x-pack/plugins/fleet/server/collectors/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,14 @@
*/

import { CoreSetup } from 'kibana/server';
import { SavedObjectsClient } from '../../../../../src/core/server';
import { ElasticsearchClient, SavedObjectsClient } from '../../../../../src/core/server';

export async function getInternalSavedObjectsClient(core: CoreSetup) {
export async function getInternalClients(
core: CoreSetup
): Promise<[SavedObjectsClient, ElasticsearchClient]> {
return core.getStartServices().then(async ([coreStart]) => {
const savedObjectsRepo = coreStart.savedObjects.createInternalRepository();
return new SavedObjectsClient(savedObjectsRepo);
const esClient = coreStart.elasticsearch.client.asInternalUser;
return [new SavedObjectsClient(savedObjectsRepo), esClient];
});
}
6 changes: 3 additions & 3 deletions x-pack/plugins/fleet/server/collectors/register.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { UsageCollectionSetup } from 'src/plugins/usage_collection/server';
import { CoreSetup } from 'kibana/server';
import { getIsAgentsEnabled } from './config_collectors';
import { AgentUsage, getAgentUsage } from './agent_collectors';
import { getInternalSavedObjectsClient } from './helpers';
import { getInternalClients } from './helpers';
import { PackageUsage, getPackageUsage } from './package_collectors';
import { FleetConfigType } from '..';

Expand All @@ -34,10 +34,10 @@ export function registerFleetUsageCollector(
type: 'fleet',
isReady: () => true,
fetch: async () => {
const soClient = await getInternalSavedObjectsClient(core);
const [soClient, esClient] = await getInternalClients(core);
return {
agents_enabled: getIsAgentsEnabled(config),
agents: await getAgentUsage(soClient),
agents: await getAgentUsage(soClient, esClient),
packages: await getPackageUsage(soClient),
};
},
Expand Down
3 changes: 3 additions & 0 deletions x-pack/plugins/fleet/server/constants/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,7 @@ export {
// Defaults
DEFAULT_AGENT_POLICY,
DEFAULT_OUTPUT,
// Fleet Server index
ENROLLMENT_API_KEYS_INDEX,
AGENTS_INDEX,
} from '../../common';
1 change: 1 addition & 0 deletions x-pack/plugins/fleet/server/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ export const config: PluginConfigDescriptor = {
registryProxyUrl: schema.maybe(schema.uri({ scheme: ['http', 'https'] })),
agents: schema.object({
enabled: schema.boolean({ defaultValue: true }),
fleetServerEnabled: schema.boolean({ defaultValue: false }),
tlsCheckDisabled: schema.boolean({ defaultValue: false }),
pollingRequestTimeout: schema.number({
defaultValue: AGENT_POLLING_REQUEST_TIMEOUT_MS,
Expand Down
7 changes: 6 additions & 1 deletion x-pack/plugins/fleet/server/mocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@
* you may not use this file except in compliance with the Elastic License.
*/

import { loggingSystemMock, savedObjectsServiceMock } from 'src/core/server/mocks';
import {
elasticsearchServiceMock,
loggingSystemMock,
savedObjectsServiceMock,
} from 'src/core/server/mocks';
import { FleetAppContext } from './plugin';
import { encryptedSavedObjectsMock } from '../../encrypted_saved_objects/server/mocks';
import { securityMock } from '../../security/server/mocks';
Expand All @@ -13,6 +17,7 @@ import { AgentPolicyServiceInterface, AgentService } from './services';

export const createAppContextStartContractMock = (): FleetAppContext => {
return {
elasticsearch: elasticsearchServiceMock.createStart(),
encryptedSavedObjectsStart: encryptedSavedObjectsMock.createStart(),
savedObjects: savedObjectsServiceMock.createStartContract(),
security: securityMock.createStart(),
Expand Down
11 changes: 11 additions & 0 deletions x-pack/plugins/fleet/server/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { first } from 'rxjs/operators';
import {
CoreSetup,
CoreStart,
ElasticsearchServiceStart,
Logger,
Plugin,
PluginInitializerContext,
Expand Down Expand Up @@ -80,6 +81,7 @@ import { agentCheckinState } from './services/agents/checkin/state';
import { registerFleetUsageCollector } from './collectors/register';
import { getInstallation } from './services/epm/packages';
import { makeRouterEnforcingSuperuser } from './routes/security';
import { runFleetServerMigration } from './services/fleet_server_migration';

export interface FleetSetupDeps {
licensing: LicensingPluginSetup;
Expand All @@ -96,6 +98,7 @@ export interface FleetStartDeps {
}

export interface FleetAppContext {
elasticsearch: ElasticsearchServiceStart;
encryptedSavedObjectsStart?: EncryptedSavedObjectsPluginStart;
encryptedSavedObjectsSetup?: EncryptedSavedObjectsPluginSetup;
security?: SecurityPluginStart;
Expand Down Expand Up @@ -276,6 +279,7 @@ export class FleetPlugin

public async start(core: CoreStart, plugins: FleetStartDeps): Promise<FleetStartContract> {
await appContextService.start({
elasticsearch: core.elasticsearch,
encryptedSavedObjectsStart: plugins.encryptedSavedObjects,
encryptedSavedObjectsSetup: this.encryptedSavedObjectsSetup,
security: plugins.security,
Expand All @@ -291,6 +295,13 @@ export class FleetPlugin
licenseService.start(this.licensing$);
agentCheckinState.start();

const fleetServerEnabled = appContextService.getConfig()?.agents?.fleetServerEnabled;
if (fleetServerEnabled) {
// We need licence to be initialized before using the SO service.
await this.licensing$.pipe(first()).toPromise();
await runFleetServerMigration();
}

return {
esIndexPatternService: new ESIndexPatternSavedObjectService(),
packageService: {
Expand Down
10 changes: 9 additions & 1 deletion x-pack/plugins/fleet/server/routes/agent/acks_handlers.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,16 @@

import { postAgentAcksHandlerBuilder } from './acks_handlers';
import {
ElasticsearchClient,
KibanaResponseFactory,
RequestHandlerContext,
SavedObjectsClientContract,
} from 'kibana/server';
import { httpServerMock, savedObjectsClientMock } from '../../../../../../src/core/server/mocks';
import {
elasticsearchServiceMock,
httpServerMock,
savedObjectsClientMock,
} from '../../../../../../src/core/server/mocks';
import { PostAgentAcksResponse } from '../../../common/types/rest_spec';
import { AckEventSchema } from '../../types/models';
import { AcksService } from '../../services/agents';
Expand Down Expand Up @@ -45,9 +50,11 @@ describe('test acks schema', () => {
describe('test acks handlers', () => {
let mockResponse: jest.Mocked<KibanaResponseFactory>;
let mockSavedObjectsClient: jest.Mocked<SavedObjectsClientContract>;
let mockElasticsearchClient: jest.Mocked<ElasticsearchClient>;

beforeEach(() => {
mockSavedObjectsClient = savedObjectsClientMock.create();
mockElasticsearchClient = elasticsearchServiceMock.createClusterClient().asInternalUser;
mockResponse = httpServerMock.createResponseFactory();
});

Expand Down Expand Up @@ -81,6 +88,7 @@ describe('test acks handlers', () => {
id: 'agent',
}),
getSavedObjectsClientContract: jest.fn().mockReturnValueOnce(mockSavedObjectsClient),
getElasticsearchClientContract: jest.fn().mockReturnValueOnce(mockElasticsearchClient),
saveAgentEvents: jest.fn(),
} as jest.Mocked<AcksService>;

Expand Down
8 changes: 7 additions & 1 deletion x-pack/plugins/fleet/server/routes/agent/acks_handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export const postAgentAcksHandlerBuilder = function (
return async (context, request, response) => {
try {
const soClient = ackService.getSavedObjectsClientContract(request);
const esClient = ackService.getElasticsearchClientContract();
const agent = await ackService.authenticateAgentWithAccessToken(soClient, request);
const agentEvents = request.body.events as AgentEvent[];

Expand All @@ -33,7 +34,12 @@ export const postAgentAcksHandlerBuilder = function (
});
}

const agentActions = await ackService.acknowledgeAgentActions(soClient, agent, agentEvents);
const agentActions = await ackService.acknowledgeAgentActions(
soClient,
esClient,
agent,
agentEvents
);

if (agentActions.length > 0) {
await ackService.saveAgentEvents(soClient, agentEvents);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,16 @@

import { NewAgentActionSchema } from '../../types/models';
import {
ElasticsearchClient,
KibanaResponseFactory,
RequestHandlerContext,
SavedObjectsClientContract,
} from 'kibana/server';
import { savedObjectsClientMock, httpServerMock } from 'src/core/server/mocks';
import {
elasticsearchServiceMock,
savedObjectsClientMock,
httpServerMock,
} from 'src/core/server/mocks';
import { ActionsService } from '../../services/agents';
import { AgentAction } from '../../../common/types/models';
import { postNewAgentActionHandlerBuilder } from './actions_handlers';
Expand Down Expand Up @@ -41,9 +46,11 @@ describe('test actions handlers schema', () => {
describe('test actions handlers', () => {
let mockResponse: jest.Mocked<KibanaResponseFactory>;
let mockSavedObjectsClient: jest.Mocked<SavedObjectsClientContract>;
let mockElasticsearchClient: jest.Mocked<ElasticsearchClient>;

beforeEach(() => {
mockSavedObjectsClient = savedObjectsClientMock.create();
mockElasticsearchClient = elasticsearchServiceMock.createClusterClient().asInternalUser;
mockResponse = httpServerMock.createResponseFactory();
});

Expand Down Expand Up @@ -84,6 +91,11 @@ describe('test actions handlers', () => {
savedObjects: {
client: mockSavedObjectsClient,
},
elasticsearch: {
client: {
asInternalUser: mockElasticsearchClient,
},
},
},
} as unknown) as RequestHandlerContext,
mockRequest,
Expand Down
3 changes: 2 additions & 1 deletion x-pack/plugins/fleet/server/routes/agent/actions_handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,9 @@ export const postNewAgentActionHandlerBuilder = function (
return async (context, request, response) => {
try {
const soClient = context.core.savedObjects.client;
const esClient = context.core.elasticsearch.client.asInternalUser;

const agent = await actionsService.getAgent(soClient, request.params.agentId);
const agent = await actionsService.getAgent(soClient, esClient, request.params.agentId);

const newAgentAction = request.body.action;

Expand Down
Loading

0 comments on commit b35509c

Please sign in to comment.