kibana/x-pack/plugins/fleet/server/plugin.ts

331 lines
11 KiB
TypeScript

/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { Observable } from 'rxjs';
import { first } from 'rxjs/operators';
import {
CoreSetup,
CoreStart,
Logger,
Plugin,
PluginInitializerContext,
SavedObjectsServiceStart,
HttpServiceSetup,
SavedObjectsClientContract,
RequestHandlerContext,
KibanaRequest,
} from 'kibana/server';
import { UsageCollectionSetup } from 'src/plugins/usage_collection/server';
import { DEFAULT_APP_CATEGORIES } from '../../../../src/core/server';
import { LicensingPluginSetup, ILicense } from '../../licensing/server';
import {
EncryptedSavedObjectsPluginStart,
EncryptedSavedObjectsPluginSetup,
} from '../../encrypted_saved_objects/server';
import { SecurityPluginSetup, SecurityPluginStart } from '../../security/server';
import { PluginSetupContract as FeaturesPluginSetup } from '../../features/server';
import {
PLUGIN_ID,
OUTPUT_SAVED_OBJECT_TYPE,
AGENT_POLICY_SAVED_OBJECT_TYPE,
PACKAGE_POLICY_SAVED_OBJECT_TYPE,
PACKAGES_SAVED_OBJECT_TYPE,
AGENT_SAVED_OBJECT_TYPE,
AGENT_EVENT_SAVED_OBJECT_TYPE,
ENROLLMENT_API_KEYS_SAVED_OBJECT_TYPE,
} from './constants';
import { registerSavedObjects, registerEncryptedSavedObjects } from './saved_objects';
import {
registerLimitedConcurrencyRoutes,
registerEPMRoutes,
registerPackagePolicyRoutes,
registerDataStreamRoutes,
registerAgentPolicyRoutes,
registerSetupRoutes,
registerAgentAPIRoutes,
registerElasticAgentRoutes,
registerEnrollmentApiKeyRoutes,
registerInstallScriptRoutes,
registerOutputRoutes,
registerSettingsRoutes,
registerAppRoutes,
} from './routes';
import {
EsAssetReference,
FleetConfigType,
NewPackagePolicy,
UpdatePackagePolicy,
} from '../common';
import {
appContextService,
licenseService,
ESIndexPatternSavedObjectService,
ESIndexPatternService,
AgentService,
AgentPolicyServiceInterface,
agentPolicyService,
packagePolicyService,
PackageService,
} from './services';
import {
getAgentStatusById,
authenticateAgentWithAccessToken,
listAgents,
getAgent,
} from './services/agents';
import { CloudSetup } from '../../cloud/server';
import { agentCheckinState } from './services/agents/checkin/state';
import { registerFleetUsageCollector } from './collectors/register';
import { getInstallation } from './services/epm/packages';
import { makeRouterEnforcingSuperuser } from './routes/security';
export interface FleetSetupDeps {
licensing: LicensingPluginSetup;
security?: SecurityPluginSetup;
features?: FeaturesPluginSetup;
encryptedSavedObjects: EncryptedSavedObjectsPluginSetup;
cloud?: CloudSetup;
usageCollection?: UsageCollectionSetup;
}
export interface FleetStartDeps {
encryptedSavedObjects: EncryptedSavedObjectsPluginStart;
security?: SecurityPluginStart;
}
export interface FleetAppContext {
encryptedSavedObjectsStart: EncryptedSavedObjectsPluginStart;
encryptedSavedObjectsSetup?: EncryptedSavedObjectsPluginSetup;
security?: SecurityPluginStart;
config$?: Observable<FleetConfigType>;
savedObjects: SavedObjectsServiceStart;
isProductionMode: PluginInitializerContext['env']['mode']['prod'];
kibanaVersion: PluginInitializerContext['env']['packageInfo']['version'];
kibanaBranch: PluginInitializerContext['env']['packageInfo']['branch'];
cloud?: CloudSetup;
logger?: Logger;
httpSetup?: HttpServiceSetup;
}
export type FleetSetupContract = void;
const allSavedObjectTypes = [
OUTPUT_SAVED_OBJECT_TYPE,
AGENT_POLICY_SAVED_OBJECT_TYPE,
PACKAGE_POLICY_SAVED_OBJECT_TYPE,
PACKAGES_SAVED_OBJECT_TYPE,
AGENT_SAVED_OBJECT_TYPE,
AGENT_EVENT_SAVED_OBJECT_TYPE,
ENROLLMENT_API_KEYS_SAVED_OBJECT_TYPE,
];
/**
* Callbacks supported by the Fleet plugin
*/
export type ExternalCallback =
| [
'packagePolicyCreate',
(
newPackagePolicy: NewPackagePolicy,
context: RequestHandlerContext,
request: KibanaRequest
) => Promise<NewPackagePolicy>
]
| [
'packagePolicyUpdate',
(
newPackagePolicy: UpdatePackagePolicy,
context: RequestHandlerContext,
request: KibanaRequest
) => Promise<UpdatePackagePolicy>
];
export type ExternalCallbacksStorage = Map<ExternalCallback[0], Set<ExternalCallback[1]>>;
/**
* Describes public Fleet plugin contract returned at the `startup` stage.
*/
export interface FleetStartContract {
esIndexPatternService: ESIndexPatternService;
packageService: PackageService;
agentService: AgentService;
/**
* Services for Fleet's package policies
*/
packagePolicyService: typeof packagePolicyService;
agentPolicyService: AgentPolicyServiceInterface;
/**
* Register callbacks for inclusion in fleet API processing
* @param args
*/
registerExternalCallback: (...args: ExternalCallback) => void;
}
export class FleetPlugin
implements Plugin<FleetSetupContract, FleetStartContract, FleetSetupDeps, FleetStartDeps> {
private licensing$!: Observable<ILicense>;
private config$: Observable<FleetConfigType>;
private cloud: CloudSetup | undefined;
private logger: Logger | undefined;
private isProductionMode: FleetAppContext['isProductionMode'];
private kibanaVersion: FleetAppContext['kibanaVersion'];
private kibanaBranch: FleetAppContext['kibanaBranch'];
private httpSetup: HttpServiceSetup | undefined;
private encryptedSavedObjectsSetup: EncryptedSavedObjectsPluginSetup | undefined;
constructor(private readonly initializerContext: PluginInitializerContext) {
this.config$ = this.initializerContext.config.create<FleetConfigType>();
this.isProductionMode = this.initializerContext.env.mode.prod;
this.kibanaVersion = this.initializerContext.env.packageInfo.version;
this.kibanaBranch = this.initializerContext.env.packageInfo.branch;
this.logger = this.initializerContext.logger.get();
}
public async setup(core: CoreSetup, deps: FleetSetupDeps) {
this.httpSetup = core.http;
this.licensing$ = deps.licensing.license$;
this.encryptedSavedObjectsSetup = deps.encryptedSavedObjects;
this.cloud = deps.cloud;
registerSavedObjects(core.savedObjects, deps.encryptedSavedObjects);
registerEncryptedSavedObjects(deps.encryptedSavedObjects);
// Register feature
// TODO: Flesh out privileges
if (deps.features) {
deps.features.registerKibanaFeature({
id: PLUGIN_ID,
name: 'Fleet',
category: DEFAULT_APP_CATEGORIES.management,
app: [PLUGIN_ID, 'kibana'],
catalogue: ['fleet'],
privileges: {
all: {
api: [`${PLUGIN_ID}-read`, `${PLUGIN_ID}-all`],
app: [PLUGIN_ID, 'kibana'],
catalogue: ['fleet'],
savedObject: {
all: allSavedObjectTypes,
read: [],
},
ui: ['show', 'read', 'write'],
},
read: {
api: [`${PLUGIN_ID}-read`],
app: [PLUGIN_ID, 'kibana'],
catalogue: ['fleet'], // TODO: check if this is actually available to read user
savedObject: {
all: [],
read: allSavedObjectTypes,
},
ui: ['show', 'read'],
},
},
});
}
const router = core.http.createRouter();
const config = await this.config$.pipe(first()).toPromise();
// Register usage collection
registerFleetUsageCollector(core, config, deps.usageCollection);
// Always register app routes for permissions checking
registerAppRoutes(router);
// For all the routes we enforce the user to have role superuser
const routerSuperuserOnly = makeRouterEnforcingSuperuser(router);
// Register rest of routes only if security is enabled
if (deps.security) {
registerSetupRoutes(routerSuperuserOnly, config);
registerAgentPolicyRoutes(routerSuperuserOnly);
registerPackagePolicyRoutes(routerSuperuserOnly);
registerOutputRoutes(routerSuperuserOnly);
registerSettingsRoutes(routerSuperuserOnly);
registerDataStreamRoutes(routerSuperuserOnly);
registerEPMRoutes(routerSuperuserOnly);
// Conditional config routes
if (config.agents.enabled) {
const isESOUsingEphemeralEncryptionKey =
deps.encryptedSavedObjects.usingEphemeralEncryptionKey;
if (isESOUsingEphemeralEncryptionKey) {
if (this.logger) {
this.logger.warn(
'Fleet APIs are disabled because the Encrypted Saved Objects plugin uses an ephemeral encryption key. Please set xpack.encryptedSavedObjects.encryptionKey in the kibana.yml or use the bin/kibana-encryption-keys command.'
);
}
} else {
// we currently only use this global interceptor if fleet is enabled
// since it would run this func on *every* req (other plugins, CSS, etc)
registerLimitedConcurrencyRoutes(core, config);
registerAgentAPIRoutes(routerSuperuserOnly, config);
registerEnrollmentApiKeyRoutes(routerSuperuserOnly);
registerInstallScriptRoutes({
router: routerSuperuserOnly,
basePath: core.http.basePath,
});
// Do not enforce superuser role for Elastic Agent routes
registerElasticAgentRoutes(router, config);
}
}
}
}
public async start(core: CoreStart, plugins: FleetStartDeps): Promise<FleetStartContract> {
await appContextService.start({
encryptedSavedObjectsStart: plugins.encryptedSavedObjects,
encryptedSavedObjectsSetup: this.encryptedSavedObjectsSetup,
security: plugins.security,
config$: this.config$,
savedObjects: core.savedObjects,
isProductionMode: this.isProductionMode,
kibanaVersion: this.kibanaVersion,
kibanaBranch: this.kibanaBranch,
httpSetup: this.httpSetup,
cloud: this.cloud,
logger: this.logger,
});
licenseService.start(this.licensing$);
agentCheckinState.start();
return {
esIndexPatternService: new ESIndexPatternSavedObjectService(),
packageService: {
getInstalledEsAssetReferences: async (
savedObjectsClient: SavedObjectsClientContract,
pkgName: string
): Promise<EsAssetReference[]> => {
const installation = await getInstallation({ savedObjectsClient, pkgName });
return installation?.installed_es || [];
},
},
agentService: {
getAgent,
listAgents,
getAgentStatusById,
authenticateAgentWithAccessToken,
},
agentPolicyService: {
get: agentPolicyService.get,
list: agentPolicyService.list,
getDefaultAgentPolicyId: agentPolicyService.getDefaultAgentPolicyId,
getFullAgentPolicy: agentPolicyService.getFullAgentPolicy,
},
packagePolicyService,
registerExternalCallback: (type: ExternalCallback[0], callback: ExternalCallback[1]) => {
return appContextService.addExternalCallback(type, callback);
},
};
}
public async stop() {
appContextService.stop();
licenseService.stop();
agentCheckinState.stop();
}
}