Skip to content

Commit

Permalink
[Fleet] Enforce superuser role for all fleet APIs (#85136) (#85349)
Browse files Browse the repository at this point in the history
  • Loading branch information
nchaulet committed Dec 9, 2020
1 parent b53e4c8 commit a0042d0
Show file tree
Hide file tree
Showing 7 changed files with 187 additions and 135 deletions.
30 changes: 18 additions & 12 deletions x-pack/plugins/fleet/server/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ import {
registerDataStreamRoutes,
registerAgentPolicyRoutes,
registerSetupRoutes,
registerAgentRoutes,
registerAgentAPIRoutes,
registerElasticAgentRoutes,
registerEnrollmentApiKeyRoutes,
registerInstallScriptRoutes,
registerOutputRoutes,
Expand Down Expand Up @@ -73,6 +74,7 @@ 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;
Expand Down Expand Up @@ -213,23 +215,25 @@ export class FleetPlugin
}

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 (this.security) {
registerSetupRoutes(router, config);
registerAgentPolicyRoutes(router);
registerPackagePolicyRoutes(router);
registerOutputRoutes(router);
registerSettingsRoutes(router);
registerDataStreamRoutes(router);
registerEPMRoutes(router);
registerSetupRoutes(routerSuperuserOnly, config);
registerAgentPolicyRoutes(routerSuperuserOnly);
registerPackagePolicyRoutes(routerSuperuserOnly);
registerOutputRoutes(routerSuperuserOnly);
registerSettingsRoutes(routerSuperuserOnly);
registerDataStreamRoutes(routerSuperuserOnly);
registerEPMRoutes(routerSuperuserOnly);

// Conditional config routes
if (config.agents.enabled) {
Expand All @@ -245,12 +249,14 @@ export class FleetPlugin
// 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);
registerAgentRoutes(router, config);
registerEnrollmentApiKeyRoutes(router);
registerAgentAPIRoutes(routerSuperuserOnly, config);
registerEnrollmentApiKeyRoutes(routerSuperuserOnly);
registerInstallScriptRoutes({
router,
router: routerSuperuserOnly,
basePath: core.http.basePath,
});
// Do not enforce superuser role for Elastic Agent routes
registerElasticAgentRoutes(router, config);
}
}
}
Expand Down
180 changes: 91 additions & 89 deletions x-pack/plugins/fleet/server/routes/agent/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ function makeValidator(jsonSchema: any) {
};
}

export const registerRoutes = (router: IRouter, config: FleetConfigType) => {
export const registerAPIRoutes = (router: IRouter, config: FleetConfigType) => {
// Get one
router.get(
{
Expand Down Expand Up @@ -119,6 +119,96 @@ export const registerRoutes = (router: IRouter, config: FleetConfigType) => {
getAgentsHandler
);

// Agent actions
router.post(
{
path: AGENT_API_ROUTES.ACTIONS_PATTERN,
validate: PostNewAgentActionRequestSchema,
options: { tags: [`access:${PLUGIN_ID}-all`] },
},
postNewAgentActionHandlerBuilder({
getAgent: AgentService.getAgent,
createAgentAction: AgentService.createAgentAction,
})
);

router.post(
{
path: AGENT_API_ROUTES.UNENROLL_PATTERN,
validate: PostAgentUnenrollRequestSchema,
options: { tags: [`access:${PLUGIN_ID}-all`] },
},
postAgentUnenrollHandler
);

router.put(
{
path: AGENT_API_ROUTES.REASSIGN_PATTERN,
validate: PutAgentReassignRequestSchema,
options: { tags: [`access:${PLUGIN_ID}-all`] },
},
putAgentsReassignHandler
);

// Get agent events
router.get(
{
path: AGENT_API_ROUTES.EVENTS_PATTERN,
validate: GetOneAgentEventsRequestSchema,
options: { tags: [`access:${PLUGIN_ID}-read`] },
},
getAgentEventsHandler
);

// Get agent status for policy
router.get(
{
path: AGENT_API_ROUTES.STATUS_PATTERN,
validate: GetAgentStatusRequestSchema,
options: { tags: [`access:${PLUGIN_ID}-read`] },
},
getAgentStatusForAgentPolicyHandler
);
// upgrade agent
router.post(
{
path: AGENT_API_ROUTES.UPGRADE_PATTERN,
validate: PostAgentUpgradeRequestSchema,
options: { tags: [`access:${PLUGIN_ID}-all`] },
},
postAgentUpgradeHandler
);
// bulk upgrade
router.post(
{
path: AGENT_API_ROUTES.BULK_UPGRADE_PATTERN,
validate: PostBulkAgentUpgradeRequestSchema,
options: { tags: [`access:${PLUGIN_ID}-all`] },
},
postBulkAgentsUpgradeHandler
);
// Bulk reassign
router.post(
{
path: AGENT_API_ROUTES.BULK_REASSIGN_PATTERN,
validate: PostBulkAgentReassignRequestSchema,
options: { tags: [`access:${PLUGIN_ID}-all`] },
},
postBulkAgentsReassignHandler
);

// Bulk unenroll
router.post(
{
path: AGENT_API_ROUTES.BULK_UNENROLL_PATTERN,
validate: PostBulkAgentUnenrollRequestSchema,
options: { tags: [`access:${PLUGIN_ID}-all`] },
},
postBulkAgentsUnenrollHandler
);
};

export const registerElasticAgentRoutes = (router: IRouter, config: FleetConfigType) => {
const pollingRequestTimeout = config.agents.pollingRequestTimeout;
// Agent checkin
router.post(
Expand Down Expand Up @@ -226,92 +316,4 @@ export const registerRoutes = (router: IRouter, config: FleetConfigType) => {
saveAgentEvents: AgentService.saveAgentEvents,
})
);

// Agent actions
router.post(
{
path: AGENT_API_ROUTES.ACTIONS_PATTERN,
validate: PostNewAgentActionRequestSchema,
options: { tags: [`access:${PLUGIN_ID}-all`] },
},
postNewAgentActionHandlerBuilder({
getAgent: AgentService.getAgent,
createAgentAction: AgentService.createAgentAction,
})
);

router.post(
{
path: AGENT_API_ROUTES.UNENROLL_PATTERN,
validate: PostAgentUnenrollRequestSchema,
options: { tags: [`access:${PLUGIN_ID}-all`] },
},
postAgentUnenrollHandler
);

router.put(
{
path: AGENT_API_ROUTES.REASSIGN_PATTERN,
validate: PutAgentReassignRequestSchema,
options: { tags: [`access:${PLUGIN_ID}-all`] },
},
putAgentsReassignHandler
);

// Get agent events
router.get(
{
path: AGENT_API_ROUTES.EVENTS_PATTERN,
validate: GetOneAgentEventsRequestSchema,
options: { tags: [`access:${PLUGIN_ID}-read`] },
},
getAgentEventsHandler
);

// Get agent status for policy
router.get(
{
path: AGENT_API_ROUTES.STATUS_PATTERN,
validate: GetAgentStatusRequestSchema,
options: { tags: [`access:${PLUGIN_ID}-read`] },
},
getAgentStatusForAgentPolicyHandler
);
// upgrade agent
router.post(
{
path: AGENT_API_ROUTES.UPGRADE_PATTERN,
validate: PostAgentUpgradeRequestSchema,
options: { tags: [`access:${PLUGIN_ID}-all`] },
},
postAgentUpgradeHandler
);
// bulk upgrade
router.post(
{
path: AGENT_API_ROUTES.BULK_UPGRADE_PATTERN,
validate: PostBulkAgentUpgradeRequestSchema,
options: { tags: [`access:${PLUGIN_ID}-all`] },
},
postBulkAgentsUpgradeHandler
);
// Bulk reassign
router.post(
{
path: AGENT_API_ROUTES.BULK_REASSIGN_PATTERN,
validate: PostBulkAgentReassignRequestSchema,
options: { tags: [`access:${PLUGIN_ID}-all`] },
},
postBulkAgentsReassignHandler
);

// Bulk unenroll
router.post(
{
path: AGENT_API_ROUTES.BULK_UNENROLL_PATTERN,
validate: PostBulkAgentUnenrollRequestSchema,
options: { tags: [`access:${PLUGIN_ID}-all`] },
},
postBulkAgentsUnenrollHandler
);
};
5 changes: 4 additions & 1 deletion x-pack/plugins/fleet/server/routes/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@ export { registerRoutes as registerPackagePolicyRoutes } from './package_policy'
export { registerRoutes as registerDataStreamRoutes } from './data_streams';
export { registerRoutes as registerEPMRoutes } from './epm';
export { registerRoutes as registerSetupRoutes } from './setup';
export { registerRoutes as registerAgentRoutes } from './agent';
export {
registerAPIRoutes as registerAgentAPIRoutes,
registerElasticAgentRoutes as registerElasticAgentRoutes,
} from './agent';
export { registerRoutes as registerEnrollmentApiKeyRoutes } from './enrollment_api_key';
export { registerRoutes as registerInstallScriptRoutes } from './install_script';
export { registerRoutes as registerOutputRoutes } from './output';
Expand Down
43 changes: 43 additions & 0 deletions x-pack/plugins/fleet/server/routes/security.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* 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 { IRouter, RequestHandler } from 'src/core/server';
import { appContextService } from '../services';

export function enforceSuperUser<T1, T2, T3>(
handler: RequestHandler<T1, T2, T3>
): RequestHandler<T1, T2, T3> {
return function enforceSuperHandler(context, req, res) {
const security = appContextService.getSecurity();
const user = security.authc.getCurrentUser(req);
if (!user) {
return res.unauthorized();
}

const userRoles = user.roles || [];
if (!userRoles.includes('superuser')) {
return res.forbidden({
body: {
message: 'Access to Fleet API require the superuser role.',
},
});
}
return handler(context, req, res);
};
}

export function makeRouterEnforcingSuperuser(router: IRouter): IRouter {
return {
get: (options, handler) => router.get(options, enforceSuperUser(handler)),
delete: (options, handler) => router.delete(options, enforceSuperUser(handler)),
post: (options, handler) => router.post(options, enforceSuperUser(handler)),
put: (options, handler) => router.put(options, enforceSuperUser(handler)),
patch: (options, handler) => router.patch(options, enforceSuperUser(handler)),
handleLegacyErrors: (handler) => router.handleLegacyErrors(handler),
getRoutes: () => router.getRoutes(),
routerPath: router.routerPath,
};
}
Loading

0 comments on commit a0042d0

Please sign in to comment.