From 5b2edd77318ee616129f75580e4372a36c1b42a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Cjeramysoucy=E2=80=9D?= Date: Thu, 25 Jul 2024 18:05:45 -0400 Subject: [PATCH 1/6] Makes the ESO key rotation API internally available in serverless --- .../encrypted_saved_objects/server/plugin.ts | 31 +++++++++-------- .../server/routes/index.ts | 2 ++ .../server/routes/key_rotation.ts | 2 ++ .../encrypted_saved_objects.ts | 33 ++++++++++++++++--- .../common/platform_security/index.ts | 26 +++++++-------- .../search/common_configs/config.group1.ts | 32 +++++++++--------- 6 files changed, 77 insertions(+), 49 deletions(-) diff --git a/x-pack/plugins/encrypted_saved_objects/server/plugin.ts b/x-pack/plugins/encrypted_saved_objects/server/plugin.ts index 7aecb03868fa8..e7e0a7b482c03 100644 --- a/x-pack/plugins/encrypted_saved_objects/server/plugin.ts +++ b/x-pack/plugins/encrypted_saved_objects/server/plugin.ts @@ -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, diff --git a/x-pack/plugins/encrypted_saved_objects/server/routes/index.ts b/x-pack/plugins/encrypted_saved_objects/server/routes/index.ts index 28f8dde589c75..d7208879e69b5 100644 --- a/x-pack/plugins/encrypted_saved_objects/server/routes/index.ts +++ b/x-pack/plugins/encrypted_saved_objects/server/routes/index.ts @@ -11,6 +11,7 @@ import type { PublicMethodsOf } from '@kbn/utility-types'; import { defineKeyRotationRoutes } from './key_rotation'; import type { ConfigType } from '../config'; import type { EncryptionKeyRotationService } from '../crypto'; +import { BuildFlavor } from '@kbn/config'; /** * Describes parameters used to define HTTP routes. @@ -20,6 +21,7 @@ export interface RouteDefinitionParams { logger: Logger; config: ConfigType; encryptionKeyRotationService: PublicMethodsOf; + buildFlavor: BuildFlavor; } export function defineRoutes(params: RouteDefinitionParams) { diff --git a/x-pack/plugins/encrypted_saved_objects/server/routes/key_rotation.ts b/x-pack/plugins/encrypted_saved_objects/server/routes/key_rotation.ts index c9c452cf9a031..e26efbb2a93b3 100644 --- a/x-pack/plugins/encrypted_saved_objects/server/routes/key_rotation.ts +++ b/x-pack/plugins/encrypted_saved_objects/server/routes/key_rotation.ts @@ -23,6 +23,7 @@ export function defineKeyRotationRoutes({ router, logger, config, + buildFlavor, }: RouteDefinitionParams) { let rotationInProgress = false; router.post( @@ -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) => { diff --git a/x-pack/test_serverless/api_integration/test_suites/common/platform_security/encrypted_saved_objects.ts b/x-pack/test_serverless/api_integration/test_suites/common/platform_security/encrypted_saved_objects.ts index ad873c2bc9ce4..86cef4596f012 100644 --- a/x-pack/test_serverless/api_integration/test_suites/common/platform_security/encrypted_saved_objects.ts +++ b/x-pack/test_serverless/api_integration/test_suites/common/platform_security/encrypted_saved_objects.ts @@ -5,6 +5,7 @@ * 2.0. */ +import expect from 'expect'; import { FtrProviderContext } from '../../../ftr_provider_context'; import { InternalRequestHeader, RoleCredentials } from '../../../../shared/services'; @@ -24,13 +25,37 @@ 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 error when we use the internal header + 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); }); }); }); diff --git a/x-pack/test_serverless/api_integration/test_suites/common/platform_security/index.ts b/x-pack/test_serverless/api_integration/test_suites/common/platform_security/index.ts index e5f3f4a86a923..41cb78f4a6cc8 100644 --- a/x-pack/test_serverless/api_integration/test_suites/common/platform_security/index.ts +++ b/x-pack/test_serverless/api_integration/test_suites/common/platform_security/index.ts @@ -11,19 +11,19 @@ export default function ({ loadTestFile }: FtrProviderContext) { describe('serverless common API', function () { this.tags(['esGate']); - loadTestFile(require.resolve('./anonymous')); - loadTestFile(require.resolve('./api_keys')); - loadTestFile(require.resolve('./authentication')); - loadTestFile(require.resolve('./authentication_http')); - loadTestFile(require.resolve('./authorization')); + // loadTestFile(require.resolve('./anonymous')); + // loadTestFile(require.resolve('./api_keys')); + // loadTestFile(require.resolve('./authentication')); + // loadTestFile(require.resolve('./authentication_http')); + // loadTestFile(require.resolve('./authorization')); loadTestFile(require.resolve('./encrypted_saved_objects')); - loadTestFile(require.resolve('./misc')); - loadTestFile(require.resolve('./response_headers')); - loadTestFile(require.resolve('./role_mappings')); - loadTestFile(require.resolve('./sessions')); - loadTestFile(require.resolve('./users')); - loadTestFile(require.resolve('./user_profiles')); - loadTestFile(require.resolve('./views')); - loadTestFile(require.resolve('./feature_check')); + // loadTestFile(require.resolve('./misc')); + // loadTestFile(require.resolve('./response_headers')); + // loadTestFile(require.resolve('./role_mappings')); + // loadTestFile(require.resolve('./sessions')); + // loadTestFile(require.resolve('./users')); + // loadTestFile(require.resolve('./user_profiles')); + // loadTestFile(require.resolve('./views')); + // loadTestFile(require.resolve('./feature_check')); }); } diff --git a/x-pack/test_serverless/api_integration/test_suites/search/common_configs/config.group1.ts b/x-pack/test_serverless/api_integration/test_suites/search/common_configs/config.group1.ts index 4167364e44793..5db5d5d34aa15 100644 --- a/x-pack/test_serverless/api_integration/test_suites/search/common_configs/config.group1.ts +++ b/x-pack/test_serverless/api_integration/test_suites/search/common_configs/config.group1.ts @@ -13,23 +13,23 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) { return { ...baseTestConfig.getAll(), testFiles: [ - require.resolve('../../common/alerting'), - require.resolve('../../common/data_view_field_editor'), - require.resolve('../../common/data_views'), - require.resolve('../../common/elasticsearch_api'), - require.resolve('../../common/index_management'), - require.resolve('../../common/kql_telemetry'), - require.resolve('../../common/management'), + // require.resolve('../../common/alerting'), + // require.resolve('../../common/data_view_field_editor'), + // require.resolve('../../common/data_views'), + // require.resolve('../../common/elasticsearch_api'), + // require.resolve('../../common/index_management'), + // require.resolve('../../common/kql_telemetry'), + // require.resolve('../../common/management'), require.resolve('../../common/platform_security'), - require.resolve('../../common/scripts_tests'), - require.resolve('../../common/search_oss'), - require.resolve('../../common/search_profiler'), - require.resolve('../../common/search_xpack'), - require.resolve('../../common/core'), - require.resolve('../../common/reporting'), - require.resolve('../../common/console'), - require.resolve('../../common/saved_objects_management'), - require.resolve('../../common/telemetry'), + // require.resolve('../../common/scripts_tests'), + // require.resolve('../../common/search_oss'), + // require.resolve('../../common/search_profiler'), + // require.resolve('../../common/search_xpack'), + // require.resolve('../../common/core'), + // require.resolve('../../common/reporting'), + // require.resolve('../../common/console'), + // require.resolve('../../common/saved_objects_management'), + // require.resolve('../../common/telemetry'), ], junit: { reportName: 'Serverless Search API Integration Tests - Common Group 1', From f7fbb26df19a5f853c282327192b2dcbf81e8e30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Cjeramysoucy=E2=80=9D?= Date: Thu, 25 Jul 2024 18:14:21 -0400 Subject: [PATCH 2/6] Uncomments test suites --- .../encrypted_saved_objects.ts | 5 ++- .../common/platform_security/index.ts | 26 +++++++-------- .../search/common_configs/config.group1.ts | 32 +++++++++---------- 3 files changed, 33 insertions(+), 30 deletions(-) diff --git a/x-pack/test_serverless/api_integration/test_suites/common/platform_security/encrypted_saved_objects.ts b/x-pack/test_serverless/api_integration/test_suites/common/platform_security/encrypted_saved_objects.ts index 86cef4596f012..51a0d3f03be8f 100644 --- a/x-pack/test_serverless/api_integration/test_suites/common/platform_security/encrypted_saved_objects.ts +++ b/x-pack/test_serverless/api_integration/test_suites/common/platform_security/encrypted_saved_objects.ts @@ -47,7 +47,10 @@ export default function ({ getService }: FtrProviderContext) { .post('/api/encrypted_saved_objects/_rotate_key') .set(internalReqHeader) .set(roleAuthc.apiKeyHeader)); - // expect a different error when we use the internal header + // 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', diff --git a/x-pack/test_serverless/api_integration/test_suites/common/platform_security/index.ts b/x-pack/test_serverless/api_integration/test_suites/common/platform_security/index.ts index 41cb78f4a6cc8..e5f3f4a86a923 100644 --- a/x-pack/test_serverless/api_integration/test_suites/common/platform_security/index.ts +++ b/x-pack/test_serverless/api_integration/test_suites/common/platform_security/index.ts @@ -11,19 +11,19 @@ export default function ({ loadTestFile }: FtrProviderContext) { describe('serverless common API', function () { this.tags(['esGate']); - // loadTestFile(require.resolve('./anonymous')); - // loadTestFile(require.resolve('./api_keys')); - // loadTestFile(require.resolve('./authentication')); - // loadTestFile(require.resolve('./authentication_http')); - // loadTestFile(require.resolve('./authorization')); + loadTestFile(require.resolve('./anonymous')); + loadTestFile(require.resolve('./api_keys')); + loadTestFile(require.resolve('./authentication')); + loadTestFile(require.resolve('./authentication_http')); + loadTestFile(require.resolve('./authorization')); loadTestFile(require.resolve('./encrypted_saved_objects')); - // loadTestFile(require.resolve('./misc')); - // loadTestFile(require.resolve('./response_headers')); - // loadTestFile(require.resolve('./role_mappings')); - // loadTestFile(require.resolve('./sessions')); - // loadTestFile(require.resolve('./users')); - // loadTestFile(require.resolve('./user_profiles')); - // loadTestFile(require.resolve('./views')); - // loadTestFile(require.resolve('./feature_check')); + loadTestFile(require.resolve('./misc')); + loadTestFile(require.resolve('./response_headers')); + loadTestFile(require.resolve('./role_mappings')); + loadTestFile(require.resolve('./sessions')); + loadTestFile(require.resolve('./users')); + loadTestFile(require.resolve('./user_profiles')); + loadTestFile(require.resolve('./views')); + loadTestFile(require.resolve('./feature_check')); }); } diff --git a/x-pack/test_serverless/api_integration/test_suites/search/common_configs/config.group1.ts b/x-pack/test_serverless/api_integration/test_suites/search/common_configs/config.group1.ts index 5db5d5d34aa15..4167364e44793 100644 --- a/x-pack/test_serverless/api_integration/test_suites/search/common_configs/config.group1.ts +++ b/x-pack/test_serverless/api_integration/test_suites/search/common_configs/config.group1.ts @@ -13,23 +13,23 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) { return { ...baseTestConfig.getAll(), testFiles: [ - // require.resolve('../../common/alerting'), - // require.resolve('../../common/data_view_field_editor'), - // require.resolve('../../common/data_views'), - // require.resolve('../../common/elasticsearch_api'), - // require.resolve('../../common/index_management'), - // require.resolve('../../common/kql_telemetry'), - // require.resolve('../../common/management'), + require.resolve('../../common/alerting'), + require.resolve('../../common/data_view_field_editor'), + require.resolve('../../common/data_views'), + require.resolve('../../common/elasticsearch_api'), + require.resolve('../../common/index_management'), + require.resolve('../../common/kql_telemetry'), + require.resolve('../../common/management'), require.resolve('../../common/platform_security'), - // require.resolve('../../common/scripts_tests'), - // require.resolve('../../common/search_oss'), - // require.resolve('../../common/search_profiler'), - // require.resolve('../../common/search_xpack'), - // require.resolve('../../common/core'), - // require.resolve('../../common/reporting'), - // require.resolve('../../common/console'), - // require.resolve('../../common/saved_objects_management'), - // require.resolve('../../common/telemetry'), + require.resolve('../../common/scripts_tests'), + require.resolve('../../common/search_oss'), + require.resolve('../../common/search_profiler'), + require.resolve('../../common/search_xpack'), + require.resolve('../../common/core'), + require.resolve('../../common/reporting'), + require.resolve('../../common/console'), + require.resolve('../../common/saved_objects_management'), + require.resolve('../../common/telemetry'), ], junit: { reportName: 'Serverless Search API Integration Tests - Common Group 1', From aee1afc5ddf91fd471acb5ce7006159bd36e7355 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Cjeramysoucy=E2=80=9D?= Date: Thu, 25 Jul 2024 18:19:49 -0400 Subject: [PATCH 3/6] Fixes typo in encryption key rotation log --- .../server/crypto/encryption_key_rotation_service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/encrypted_saved_objects/server/crypto/encryption_key_rotation_service.ts b/x-pack/plugins/encrypted_saved_objects/server/crypto/encryption_key_rotation_service.ts index d8fa12a3e4973..80275ce3a91e4 100644 --- a/x-pack/plugins/encrypted_saved_objects/server/crypto/encryption_key_rotation_service.ts +++ b/x-pack/plugins/encrypted_saved_objects/server/crypto/encryption_key_rotation_service.ts @@ -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; From aa6832a3633e823987f1dc5da0433ae30f3fc457 Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Thu, 25 Jul 2024 22:31:13 +0000 Subject: [PATCH 4/6] [CI] Auto-commit changed files from 'node scripts/lint_ts_projects --fix' --- x-pack/plugins/encrypted_saved_objects/tsconfig.json | 1 + 1 file changed, 1 insertion(+) diff --git a/x-pack/plugins/encrypted_saved_objects/tsconfig.json b/x-pack/plugins/encrypted_saved_objects/tsconfig.json index 83cdcd6225850..d2115146a4a42 100644 --- a/x-pack/plugins/encrypted_saved_objects/tsconfig.json +++ b/x-pack/plugins/encrypted_saved_objects/tsconfig.json @@ -14,6 +14,7 @@ "@kbn/core-saved-objects-api-server-mocks", "@kbn/core-security-common", "@kbn/test-jest-helpers", + "@kbn/config", ], "exclude": [ "target/**/*", From 5fb7bae1769dd589ae3997ff95255e577eac49d1 Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Thu, 25 Jul 2024 23:28:35 +0000 Subject: [PATCH 5/6] [CI] Auto-commit changed files from 'node scripts/eslint --no-cache --fix' --- x-pack/plugins/encrypted_saved_objects/server/routes/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/encrypted_saved_objects/server/routes/index.ts b/x-pack/plugins/encrypted_saved_objects/server/routes/index.ts index d7208879e69b5..14d1933e8b765 100644 --- a/x-pack/plugins/encrypted_saved_objects/server/routes/index.ts +++ b/x-pack/plugins/encrypted_saved_objects/server/routes/index.ts @@ -5,13 +5,13 @@ * 2.0. */ +import { BuildFlavor } from '@kbn/config'; import type { IRouter, Logger } from '@kbn/core/server'; import type { PublicMethodsOf } from '@kbn/utility-types'; import { defineKeyRotationRoutes } from './key_rotation'; import type { ConfigType } from '../config'; import type { EncryptionKeyRotationService } from '../crypto'; -import { BuildFlavor } from '@kbn/config'; /** * Describes parameters used to define HTTP routes. From e4a8a7979c5f49e79048350450c384a4b1c1b16a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Cjeramysoucy=E2=80=9D?= Date: Thu, 25 Jul 2024 21:52:29 -0400 Subject: [PATCH 6/6] Fixes typing in mock --- .../plugins/encrypted_saved_objects/server/routes/index.mock.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/x-pack/plugins/encrypted_saved_objects/server/routes/index.mock.ts b/x-pack/plugins/encrypted_saved_objects/server/routes/index.mock.ts index 822427687759f..e6263521f690d 100644 --- a/x-pack/plugins/encrypted_saved_objects/server/routes/index.mock.ts +++ b/x-pack/plugins/encrypted_saved_objects/server/routes/index.mock.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { BuildFlavor } from '@kbn/config'; import { httpServiceMock, loggingSystemMock } from '@kbn/core/server/mocks'; import type { ConfigType } from '../config'; @@ -17,5 +18,6 @@ export const routeDefinitionParamsMock = { logger: loggingSystemMock.create().get(), config: ConfigSchema.validate(config) as ConfigType, encryptionKeyRotationService: encryptionKeyRotationServiceMock.create(), + buildFlavor: 'traditional' as BuildFlavor, }), };