Skip to content

Commit

Permalink
Expose the encrypted saved objects key rotation API as internal in se…
Browse files Browse the repository at this point in the history
…rverless (elastic#189238)

## Summary

In order to begin work for encryption key rotation in serverless, we
will need to expose the endpoint use to bulk re-encrypt saved objects.
This endpoint was previously unregistered in serverless. This PR
registers the API and marks it as internal when a serverless build
flavor is detected.


### Tests
-
x-pack/test_serverless/api_integration/test_suites/common/platform_security/encrypted_saved_objects.ts

---------

Co-authored-by: kibanamachine <[email protected]>
  • Loading branch information
jeramysoucy and kibanamachine authored Jul 26, 2024
1 parent 10bfb4b commit b51c503
Show file tree
Hide file tree
Showing 7 changed files with 55 additions and 21 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ export class EncryptionKeyRotationService {
}

this.options.logger.info(
`Encryption key rotation is completed. ${result.successful} objects out ouf ${result.total} were successfully re-encrypted with the primary encryption key and ${result.failed} objects failed.`
`Encryption key rotation is completed. ${result.successful} objects out of ${result.total} were successfully re-encrypted with the primary encryption key and ${result.failed} objects failed.`
);

return result;
Expand Down
31 changes: 15 additions & 16 deletions x-pack/plugins/encrypted_saved_objects/server/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,22 +110,21 @@ export class EncryptedSavedObjectsPlugin
getStartServices: core.getStartServices,
});

// In the serverless environment, the encryption keys for saved objects is managed internally and never
// exposed to users and administrators, eliminating the need for any public Encrypted Saved Objects HTTP APIs
if (this.initializerContext.env.packageInfo.buildFlavor !== 'serverless') {
defineRoutes({
router: core.http.createRouter(),
logger: this.initializerContext.logger.get('routes'),
encryptionKeyRotationService: Object.freeze(
new EncryptionKeyRotationService({
logger: this.logger.get('key-rotation-service'),
service,
getStartServices: core.getStartServices,
})
),
config,
});
}
// Expose the key rotation route for both stateful and serverless environments
// The endpoint requires admin privileges, and is internal only in serverless
defineRoutes({
router: core.http.createRouter(),
logger: this.initializerContext.logger.get('routes'),
encryptionKeyRotationService: Object.freeze(
new EncryptionKeyRotationService({
logger: this.logger.get('key-rotation-service'),
service,
getStartServices: core.getStartServices,
})
),
config,
buildFlavor: this.initializerContext.env.packageInfo.buildFlavor,
});

return {
canEncrypt,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
* 2.0.
*/

import { BuildFlavor } from '@kbn/config';
import { httpServiceMock, loggingSystemMock } from '@kbn/core/server/mocks';

import type { ConfigType } from '../config';
Expand All @@ -17,5 +18,6 @@ export const routeDefinitionParamsMock = {
logger: loggingSystemMock.create().get(),
config: ConfigSchema.validate(config) as ConfigType,
encryptionKeyRotationService: encryptionKeyRotationServiceMock.create(),
buildFlavor: 'traditional' as BuildFlavor,
}),
};
2 changes: 2 additions & 0 deletions x-pack/plugins/encrypted_saved_objects/server/routes/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
* 2.0.
*/

import { BuildFlavor } from '@kbn/config';
import type { IRouter, Logger } from '@kbn/core/server';
import type { PublicMethodsOf } from '@kbn/utility-types';

Expand All @@ -20,6 +21,7 @@ export interface RouteDefinitionParams {
logger: Logger;
config: ConfigType;
encryptionKeyRotationService: PublicMethodsOf<EncryptionKeyRotationService>;
buildFlavor: BuildFlavor;
}

export function defineRoutes(params: RouteDefinitionParams) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export function defineKeyRotationRoutes({
router,
logger,
config,
buildFlavor,
}: RouteDefinitionParams) {
let rotationInProgress = false;
router.post(
Expand All @@ -41,6 +42,7 @@ export function defineKeyRotationRoutes({
options: {
tags: ['access:rotateEncryptionKey', 'oas-tag:saved objects'],
description: `Rotate a key for encrypted saved objects`,
access: buildFlavor === 'serverless' ? 'internal' : undefined,
},
},
async (context, request, response) => {
Expand Down
1 change: 1 addition & 0 deletions x-pack/plugins/encrypted_saved_objects/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
"@kbn/core-saved-objects-api-server-mocks",
"@kbn/core-security-common",
"@kbn/test-jest-helpers",
"@kbn/config",
],
"exclude": [
"target/**/*",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
* 2.0.
*/

import expect from 'expect';
import { FtrProviderContext } from '../../../ftr_provider_context';
import { InternalRequestHeader, RoleCredentials } from '../../../../shared/services';

Expand All @@ -24,13 +25,40 @@ export default function ({ getService }: FtrProviderContext) {
await svlUserManager.invalidateM2mApiKeyWithRoleScope(roleAuthc);
});
describe('route access', () => {
describe('disabled', () => {
describe('internal', () => {
it('rotate key', async () => {
const { body, status } = await supertestWithoutAuth
let body: unknown;
let status: number;

({ body, status } = await supertestWithoutAuth
.post('/api/encrypted_saved_objects/_rotate_key')
// .set(internalReqHeader)
.set(roleAuthc.apiKeyHeader));
// svlCommonApi.assertApiNotFound(body, status);
// expect a rejection because we're not using the internal header
expect(body).toEqual({
statusCode: 400,
error: 'Bad Request',
message: expect.stringContaining('Request must contain a kbn-xsrf header.'),
});
expect(status).toBe(400);

({ body, status } = await supertestWithoutAuth
.post('/api/encrypted_saved_objects/_rotate_key')
.set(internalReqHeader)
.set(roleAuthc.apiKeyHeader);
svlCommonApi.assertApiNotFound(body, status);
.set(roleAuthc.apiKeyHeader));
// expect a different, legitimate error when we use the internal header
// the config does not contain decryptionOnlyKeys, so when the API is
// called successfully, it will error for this reason, and not for an
// access or or missing header reason
expect(body).toEqual({
statusCode: 400,
error: 'Bad Request',
message: expect.stringContaining(
'Kibana is not configured to support encryption key rotation. Update `kibana.yml` to include `xpack.encryptedSavedObjects.keyRotation.decryptionOnlyKeys` to rotate your encryption keys.'
),
});
expect(status).toBe(400);
});
});
});
Expand Down

0 comments on commit b51c503

Please sign in to comment.