Skip to content

Commit

Permalink
Use kibana_system user for Fleet setup and package operations (#112808)…
Browse files Browse the repository at this point in the history
… (#115165)

Co-authored-by: Kibana Machine <[email protected]>

Co-authored-by: Josh Dover <[email protected]>
  • Loading branch information
kibanamachine and joshdover authored Oct 15, 2021
1 parent 8a23b95 commit e1d6830
Show file tree
Hide file tree
Showing 13 changed files with 311 additions and 218 deletions.
18 changes: 16 additions & 2 deletions x-pack/plugins/fleet/server/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ import {
registerPreconfigurationRoutes,
} from './routes';

import type { ExternalCallback } from './types';
import type { ExternalCallback, FleetRequestHandlerContext } from './types';
import type {
ESIndexPatternService,
AgentService,
Expand Down Expand Up @@ -231,7 +231,21 @@ export class FleetPlugin
});
}

const router = core.http.createRouter();
core.http.registerRouteHandlerContext<FleetRequestHandlerContext, 'fleet'>(
'fleet',
(coreContext, request) => ({
epm: {
// Use a lazy getter to avoid constructing this client when not used by a request handler
get internalSoClient() {
return appContextService
.getSavedObjects()
.getScopedClient(request, { excludedWrappers: ['security'] });
},
},
})
);

const router = core.http.createRouter<FleetRequestHandlerContext>();

// Register usage collection
registerFleetUsageCollector(core, config, deps.usageCollection);
Expand Down
233 changes: 113 additions & 120 deletions x-pack/plugins/fleet/server/routes/epm/handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import path from 'path';

import type { TypeOf } from '@kbn/config-schema';
import mime from 'mime-types';
import type { RequestHandler, ResponseHeaders, KnownHeaders } from 'src/core/server';
import type { ResponseHeaders, KnownHeaders } from 'src/core/server';

import type {
GetInfoResponse,
Expand All @@ -34,6 +34,7 @@ import type {
DeletePackageRequestSchema,
BulkUpgradePackagesFromRegistryRequestSchema,
GetStatsRequestSchema,
FleetRequestHandler,
UpdatePackageRequestSchema,
} from '../../types';
import {
Expand All @@ -57,7 +58,7 @@ import { getAsset } from '../../services/epm/archive/storage';
import { getPackageUsageStats } from '../../services/epm/packages/get';
import { updatePackage } from '../../services/epm/packages/update';

export const getCategoriesHandler: RequestHandler<
export const getCategoriesHandler: FleetRequestHandler<
undefined,
TypeOf<typeof GetCategoriesRequestSchema.query>
> = async (context, request, response) => {
Expand All @@ -72,12 +73,12 @@ export const getCategoriesHandler: RequestHandler<
}
};

export const getListHandler: RequestHandler<
export const getListHandler: FleetRequestHandler<
undefined,
TypeOf<typeof GetPackagesRequestSchema.query>
> = async (context, request, response) => {
try {
const savedObjectsClient = context.core.savedObjects.client;
const savedObjectsClient = context.fleet.epm.internalSoClient;
const res = await getPackages({
savedObjectsClient,
...request.query,
Expand All @@ -93,9 +94,9 @@ export const getListHandler: RequestHandler<
}
};

export const getLimitedListHandler: RequestHandler = async (context, request, response) => {
export const getLimitedListHandler: FleetRequestHandler = async (context, request, response) => {
try {
const savedObjectsClient = context.core.savedObjects.client;
const savedObjectsClient = context.fleet.epm.internalSoClient;
const res = await getLimitedPackages({ savedObjectsClient });
const body: GetLimitedPackagesResponse = {
response: res,
Expand All @@ -108,110 +109,105 @@ export const getLimitedListHandler: RequestHandler = async (context, request, re
}
};

export const getFileHandler: RequestHandler<TypeOf<typeof GetFileRequestSchema.params>> = async (
context,
request,
response
) => {
try {
const { pkgName, pkgVersion, filePath } = request.params;
const savedObjectsClient = context.core.savedObjects.client;
const installation = await getInstallation({ savedObjectsClient, pkgName });
const useLocalFile = pkgVersion === installation?.version;
export const getFileHandler: FleetRequestHandler<TypeOf<typeof GetFileRequestSchema.params>> =
async (context, request, response) => {
try {
const { pkgName, pkgVersion, filePath } = request.params;
const savedObjectsClient = context.fleet.epm.internalSoClient;
const installation = await getInstallation({ savedObjectsClient, pkgName });
const useLocalFile = pkgVersion === installation?.version;

if (useLocalFile) {
const assetPath = `${pkgName}-${pkgVersion}/${filePath}`;
const fileBuffer = getArchiveEntry(assetPath);
// only pull local installation if we don't have it cached
const storedAsset =
!fileBuffer && (await getAsset({ savedObjectsClient, path: assetPath }));

if (useLocalFile) {
const assetPath = `${pkgName}-${pkgVersion}/${filePath}`;
const fileBuffer = getArchiveEntry(assetPath);
// only pull local installation if we don't have it cached
const storedAsset = !fileBuffer && (await getAsset({ savedObjectsClient, path: assetPath }));
// error, if neither is available
if (!fileBuffer && !storedAsset) {
return response.custom({
body: `installed package file not found: ${filePath}`,
statusCode: 404,
});
}

// if storedAsset is not available, fileBuffer *must* be
// b/c we error if we don't have at least one, and storedAsset is the least likely
const { buffer, contentType } = storedAsset
? {
contentType: storedAsset.media_type,
buffer: storedAsset.data_utf8
? Buffer.from(storedAsset.data_utf8, 'utf8')
: Buffer.from(storedAsset.data_base64, 'base64'),
}
: {
contentType: mime.contentType(path.extname(assetPath)),
buffer: fileBuffer,
};

if (!contentType) {
return response.custom({
body: `unknown content type for file: ${filePath}`,
statusCode: 400,
});
}

// error, if neither is available
if (!fileBuffer && !storedAsset) {
return response.custom({
body: `installed package file not found: ${filePath}`,
statusCode: 404,
body: buffer,
statusCode: 200,
headers: {
'cache-control': 'max-age=10, public',
'content-type': contentType,
},
});
}

// if storedAsset is not available, fileBuffer *must* be
// b/c we error if we don't have at least one, and storedAsset is the least likely
const { buffer, contentType } = storedAsset
? {
contentType: storedAsset.media_type,
buffer: storedAsset.data_utf8
? Buffer.from(storedAsset.data_utf8, 'utf8')
: Buffer.from(storedAsset.data_base64, 'base64'),
} else {
const registryResponse = await getFile(pkgName, pkgVersion, filePath);
const headersToProxy: KnownHeaders[] = ['content-type', 'cache-control'];
const proxiedHeaders = headersToProxy.reduce((headers, knownHeader) => {
const value = registryResponse.headers.get(knownHeader);
if (value !== null) {
headers[knownHeader] = value;
}
: {
contentType: mime.contentType(path.extname(assetPath)),
buffer: fileBuffer,
};
return headers;
}, {} as ResponseHeaders);

if (!contentType) {
return response.custom({
body: `unknown content type for file: ${filePath}`,
statusCode: 400,
body: registryResponse.body,
statusCode: registryResponse.status,
headers: proxiedHeaders,
});
}

return response.custom({
body: buffer,
statusCode: 200,
headers: {
'cache-control': 'max-age=10, public',
'content-type': contentType,
},
});
} else {
const registryResponse = await getFile(pkgName, pkgVersion, filePath);
const headersToProxy: KnownHeaders[] = ['content-type', 'cache-control'];
const proxiedHeaders = headersToProxy.reduce((headers, knownHeader) => {
const value = registryResponse.headers.get(knownHeader);
if (value !== null) {
headers[knownHeader] = value;
}
return headers;
}, {} as ResponseHeaders);

return response.custom({
body: registryResponse.body,
statusCode: registryResponse.status,
headers: proxiedHeaders,
});
} catch (error) {
return defaultIngestErrorHandler({ error, response });
}
} catch (error) {
return defaultIngestErrorHandler({ error, response });
}
};
};

export const getInfoHandler: RequestHandler<TypeOf<typeof GetInfoRequestSchema.params>> = async (
context,
request,
response
) => {
try {
const { pkgkey } = request.params;
const savedObjectsClient = context.core.savedObjects.client;
// TODO: change epm API to /packageName/version so we don't need to do this
const { pkgName, pkgVersion } = splitPkgKey(pkgkey);
const res = await getPackageInfo({ savedObjectsClient, pkgName, pkgVersion });
const body: GetInfoResponse = {
response: res,
};
return response.ok({ body });
} catch (error) {
return defaultIngestErrorHandler({ error, response });
}
};
export const getInfoHandler: FleetRequestHandler<TypeOf<typeof GetInfoRequestSchema.params>> =
async (context, request, response) => {
try {
const { pkgkey } = request.params;
const savedObjectsClient = context.fleet.epm.internalSoClient;
// TODO: change epm API to /packageName/version so we don't need to do this
const { pkgName, pkgVersion } = splitPkgKey(pkgkey);
const res = await getPackageInfo({ savedObjectsClient, pkgName, pkgVersion });
const body: GetInfoResponse = {
response: res,
};
return response.ok({ body });
} catch (error) {
return defaultIngestErrorHandler({ error, response });
}
};

export const updatePackageHandler: RequestHandler<
export const updatePackageHandler: FleetRequestHandler<
TypeOf<typeof UpdatePackageRequestSchema.params>,
unknown,
TypeOf<typeof UpdatePackageRequestSchema.body>
> = async (context, request, response) => {
try {
const { pkgkey } = request.params;
const savedObjectsClient = context.core.savedObjects.client;
const savedObjectsClient = context.fleet.epm.internalSoClient;

const { pkgName } = splitPkgKey(pkgkey);

Expand All @@ -226,30 +222,27 @@ export const updatePackageHandler: RequestHandler<
}
};

export const getStatsHandler: RequestHandler<TypeOf<typeof GetStatsRequestSchema.params>> = async (
context,
request,
response
) => {
try {
const { pkgName } = request.params;
const savedObjectsClient = context.core.savedObjects.client;
const body: GetStatsResponse = {
response: await getPackageUsageStats({ savedObjectsClient, pkgName }),
};
return response.ok({ body });
} catch (error) {
return defaultIngestErrorHandler({ error, response });
}
};
export const getStatsHandler: FleetRequestHandler<TypeOf<typeof GetStatsRequestSchema.params>> =
async (context, request, response) => {
try {
const { pkgName } = request.params;
const savedObjectsClient = context.fleet.epm.internalSoClient;
const body: GetStatsResponse = {
response: await getPackageUsageStats({ savedObjectsClient, pkgName }),
};
return response.ok({ body });
} catch (error) {
return defaultIngestErrorHandler({ error, response });
}
};

export const installPackageFromRegistryHandler: RequestHandler<
export const installPackageFromRegistryHandler: FleetRequestHandler<
TypeOf<typeof InstallPackageFromRegistryRequestSchema.params>,
undefined,
TypeOf<typeof InstallPackageFromRegistryRequestSchema.body>
> = async (context, request, response) => {
const savedObjectsClient = context.core.savedObjects.client;
const esClient = context.core.elasticsearch.client.asCurrentUser;
const savedObjectsClient = context.fleet.epm.internalSoClient;
const esClient = context.core.elasticsearch.client.asInternalUser;
const { pkgkey } = request.params;

const res = await installPackage({
Expand Down Expand Up @@ -284,13 +277,13 @@ const bulkInstallServiceResponseToHttpEntry = (
}
};

export const bulkInstallPackagesFromRegistryHandler: RequestHandler<
export const bulkInstallPackagesFromRegistryHandler: FleetRequestHandler<
undefined,
undefined,
TypeOf<typeof BulkUpgradePackagesFromRegistryRequestSchema.body>
> = async (context, request, response) => {
const savedObjectsClient = context.core.savedObjects.client;
const esClient = context.core.elasticsearch.client.asCurrentUser;
const savedObjectsClient = context.fleet.epm.internalSoClient;
const esClient = context.core.elasticsearch.client.asInternalUser;
const bulkInstalledResponses = await bulkInstallPackages({
savedObjectsClient,
esClient,
Expand All @@ -303,7 +296,7 @@ export const bulkInstallPackagesFromRegistryHandler: RequestHandler<
return response.ok({ body });
};

export const installPackageByUploadHandler: RequestHandler<
export const installPackageByUploadHandler: FleetRequestHandler<
undefined,
undefined,
TypeOf<typeof InstallPackageByUploadRequestSchema.body>
Expand All @@ -314,8 +307,8 @@ export const installPackageByUploadHandler: RequestHandler<
body: { message: 'Requires Enterprise license' },
});
}
const savedObjectsClient = context.core.savedObjects.client;
const esClient = context.core.elasticsearch.client.asCurrentUser;
const savedObjectsClient = context.fleet.epm.internalSoClient;
const esClient = context.core.elasticsearch.client.asInternalUser;
const contentType = request.headers['content-type'] as string; // from types it could also be string[] or undefined but this is checked later
const archiveBuffer = Buffer.from(request.body);

Expand All @@ -336,15 +329,15 @@ export const installPackageByUploadHandler: RequestHandler<
}
};

export const deletePackageHandler: RequestHandler<
export const deletePackageHandler: FleetRequestHandler<
TypeOf<typeof DeletePackageRequestSchema.params>,
undefined,
TypeOf<typeof DeletePackageRequestSchema.body>
> = async (context, request, response) => {
try {
const { pkgkey } = request.params;
const savedObjectsClient = context.core.savedObjects.client;
const esClient = context.core.elasticsearch.client.asCurrentUser;
const savedObjectsClient = context.fleet.epm.internalSoClient;
const esClient = context.core.elasticsearch.client.asInternalUser;
const res = await removeInstallation({
savedObjectsClient,
pkgkey,
Expand Down
3 changes: 2 additions & 1 deletion x-pack/plugins/fleet/server/routes/epm/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import type { IRouter } from 'src/core/server';

import { PLUGIN_ID, EPM_API_ROUTES } from '../../constants';
import type { FleetRequestHandlerContext } from '../../types';
import {
GetCategoriesRequestSchema,
GetPackagesRequestSchema,
Expand Down Expand Up @@ -38,7 +39,7 @@ import {

const MAX_FILE_SIZE_BYTES = 104857600; // 100MB

export const registerRoutes = (router: IRouter) => {
export const registerRoutes = (router: IRouter<FleetRequestHandlerContext>) => {
router.get(
{
path: EPM_API_ROUTES.CATEGORIES_PATTERN,
Expand Down
Loading

0 comments on commit e1d6830

Please sign in to comment.