mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 09:19:04 -04:00
# Backport This will backport the following commits from `main` to `8.x`: - [Fixes bulk re-encryption for encrypted objects located in all spaces (#217625)](https://github.com/elastic/kibana/pull/217625) <!--- Backport version: 9.6.6 --> ### Questions ? Please refer to the [Backport tool documentation](https://github.com/sorenlouv/backport) <!--BACKPORT [{"author":{"name":"Jeramy Soucy","email":"jeramy.soucy@elastic.co"},"sourceCommit":{"committedDate":"2025-04-14T19:20:38Z","message":"Fixes bulk re-encryption for encrypted objects located in all spaces (#217625)\n\nCloses #215534\n\n## Summary\n\nThe Encrypted Saved Objects Key Rotation service makes use of the Saved\nObjects Bulk Update API to re-encrypt objects. Bulk update supports an\noptional 'namespace' parameter, per-object, defining the space to access\na specific object. This allows objects outside of the current space to\nbe affected in the update operation. The Key Rotation service leverages\nthis optional parameter for each object to ensure that the re-encryption\noperation is not limited to the current space.\n\nHowever, should a multi-namespace encrypted object reside in all spaces,\nthe only value in the object's namespaces property is the\n`ALL_NAMESPACES_STRING` constant '*'. As this is not a valid single\nnamespace, the Bulk Update operation will skip updating the object.\n\nPR resolves the issue by only providing a object namespace for objects\nthat do not reside in all spaces. Objects that reside in all spaces can\nbe accessed from the current space without the need for an override.\n\nThis PR also updates unit tests to account for this case.\n\n### Testing\n- [x] Set the encrypted saved objects encryption key to a known value\n(either in kibana.yml or kibana.dev.yml). For example:\n```\nxpack.encryptedSavedObjects:\n encryptionKey: \"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\" \n```\n- [x] Start ES & Kibana\n- [x] You will need to set up a Fleet agent policy and create a\nsynthetics location and monitor. The UI will guide you through this when\nyou navigate to Observability -> Synthetics\n- [x] Create a synthetics parameter, Observability -> Synthetics ->\nSettings, Global Parameters tab. Use anything for a value, but be sure\nto check the `Share across spaces` option.\n- [x] Update the kibana config to change the encryption key, and use the\nold key as a decryption-only key\n```\nxpack.encryptedSavedObjects:\n encryptionKey: \"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\" \n keyRotation:\n decryptionOnlyKeys: [\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"] \n```\n- [x] Wait for Kibana to restart\n- [x] Call the key rotation HTTP API as a privileged user (I just used\nthe `elastic` superuser account)\n\n`[you_kibana_endpoint]/api/encrypted_saved_objects/_rotate_key?type=synthetics-param`\n- [x] Verify that 1 out of 1 objects were processed with 0 failures.\n- [x] Repeat these steps from Main and note that 0 of 1 objects\nsucceeded, and there is 1 failure\n\n### Release Note\nFixes an issue where the Saved Objects Rotate Encryption Key API would\nnot affect sharable encrypted object types that exist in all spaces.\n\n---------\n\nCo-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>","sha":"1176625dcaf8ec8ca4e4aa0b1324279ab0f2def3","branchLabelMapping":{"^v9.1.0$":"main","^v8.19.0$":"8.x","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:fix","Team:Security","backport:all-open","v9.1.0"],"title":"Fixes bulk re-encryption for encrypted objects located in all spaces","number":217625,"url":"https://github.com/elastic/kibana/pull/217625","mergeCommit":{"message":"Fixes bulk re-encryption for encrypted objects located in all spaces (#217625)\n\nCloses #215534\n\n## Summary\n\nThe Encrypted Saved Objects Key Rotation service makes use of the Saved\nObjects Bulk Update API to re-encrypt objects. Bulk update supports an\noptional 'namespace' parameter, per-object, defining the space to access\na specific object. This allows objects outside of the current space to\nbe affected in the update operation. The Key Rotation service leverages\nthis optional parameter for each object to ensure that the re-encryption\noperation is not limited to the current space.\n\nHowever, should a multi-namespace encrypted object reside in all spaces,\nthe only value in the object's namespaces property is the\n`ALL_NAMESPACES_STRING` constant '*'. As this is not a valid single\nnamespace, the Bulk Update operation will skip updating the object.\n\nPR resolves the issue by only providing a object namespace for objects\nthat do not reside in all spaces. Objects that reside in all spaces can\nbe accessed from the current space without the need for an override.\n\nThis PR also updates unit tests to account for this case.\n\n### Testing\n- [x] Set the encrypted saved objects encryption key to a known value\n(either in kibana.yml or kibana.dev.yml). For example:\n```\nxpack.encryptedSavedObjects:\n encryptionKey: \"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\" \n```\n- [x] Start ES & Kibana\n- [x] You will need to set up a Fleet agent policy and create a\nsynthetics location and monitor. The UI will guide you through this when\nyou navigate to Observability -> Synthetics\n- [x] Create a synthetics parameter, Observability -> Synthetics ->\nSettings, Global Parameters tab. Use anything for a value, but be sure\nto check the `Share across spaces` option.\n- [x] Update the kibana config to change the encryption key, and use the\nold key as a decryption-only key\n```\nxpack.encryptedSavedObjects:\n encryptionKey: \"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\" \n keyRotation:\n decryptionOnlyKeys: [\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"] \n```\n- [x] Wait for Kibana to restart\n- [x] Call the key rotation HTTP API as a privileged user (I just used\nthe `elastic` superuser account)\n\n`[you_kibana_endpoint]/api/encrypted_saved_objects/_rotate_key?type=synthetics-param`\n- [x] Verify that 1 out of 1 objects were processed with 0 failures.\n- [x] Repeat these steps from Main and note that 0 of 1 objects\nsucceeded, and there is 1 failure\n\n### Release Note\nFixes an issue where the Saved Objects Rotate Encryption Key API would\nnot affect sharable encrypted object types that exist in all spaces.\n\n---------\n\nCo-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>","sha":"1176625dcaf8ec8ca4e4aa0b1324279ab0f2def3"}},"sourceBranch":"main","suggestedTargetBranches":[],"targetPullRequestStates":[{"branch":"main","label":"v9.1.0","branchLabelMappingKey":"^v9.1.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/217625","number":217625,"mergeCommit":{"message":"Fixes bulk re-encryption for encrypted objects located in all spaces (#217625)\n\nCloses #215534\n\n## Summary\n\nThe Encrypted Saved Objects Key Rotation service makes use of the Saved\nObjects Bulk Update API to re-encrypt objects. Bulk update supports an\noptional 'namespace' parameter, per-object, defining the space to access\na specific object. This allows objects outside of the current space to\nbe affected in the update operation. The Key Rotation service leverages\nthis optional parameter for each object to ensure that the re-encryption\noperation is not limited to the current space.\n\nHowever, should a multi-namespace encrypted object reside in all spaces,\nthe only value in the object's namespaces property is the\n`ALL_NAMESPACES_STRING` constant '*'. As this is not a valid single\nnamespace, the Bulk Update operation will skip updating the object.\n\nPR resolves the issue by only providing a object namespace for objects\nthat do not reside in all spaces. Objects that reside in all spaces can\nbe accessed from the current space without the need for an override.\n\nThis PR also updates unit tests to account for this case.\n\n### Testing\n- [x] Set the encrypted saved objects encryption key to a known value\n(either in kibana.yml or kibana.dev.yml). For example:\n```\nxpack.encryptedSavedObjects:\n encryptionKey: \"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\" \n```\n- [x] Start ES & Kibana\n- [x] You will need to set up a Fleet agent policy and create a\nsynthetics location and monitor. The UI will guide you through this when\nyou navigate to Observability -> Synthetics\n- [x] Create a synthetics parameter, Observability -> Synthetics ->\nSettings, Global Parameters tab. Use anything for a value, but be sure\nto check the `Share across spaces` option.\n- [x] Update the kibana config to change the encryption key, and use the\nold key as a decryption-only key\n```\nxpack.encryptedSavedObjects:\n encryptionKey: \"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\" \n keyRotation:\n decryptionOnlyKeys: [\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"] \n```\n- [x] Wait for Kibana to restart\n- [x] Call the key rotation HTTP API as a privileged user (I just used\nthe `elastic` superuser account)\n\n`[you_kibana_endpoint]/api/encrypted_saved_objects/_rotate_key?type=synthetics-param`\n- [x] Verify that 1 out of 1 objects were processed with 0 failures.\n- [x] Repeat these steps from Main and note that 0 of 1 objects\nsucceeded, and there is 1 failure\n\n### Release Note\nFixes an issue where the Saved Objects Rotate Encryption Key API would\nnot affect sharable encrypted object types that exist in all spaces.\n\n---------\n\nCo-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>","sha":"1176625dcaf8ec8ca4e4aa0b1324279ab0f2def3"}}]}] BACKPORT--> --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
a58c356711
commit
cb0bdae67d
3 changed files with 24 additions and 9 deletions
|
@ -63,8 +63,11 @@ beforeEach(() => {
|
|||
{ name: 'type-id-4', namespaceType: 'multiple', mappings: { properties: {} }, hidden: true },
|
||||
{ name: 'type-id-5', namespaceType: 'single', mappings: { properties: {} }, hidden: false },
|
||||
{ name: 'type-id-6', namespaceType: 'single', mappings: { properties: {} }, hidden: true },
|
||||
{ name: 'type-id-7', namespaceType: 'multiple', mappings: { properties: {} }, hidden: false },
|
||||
]);
|
||||
typeRegistryMock.isSingleNamespace.mockImplementation((type) => type !== 'type-id-4');
|
||||
typeRegistryMock.isSingleNamespace.mockImplementation(
|
||||
(type) => type !== 'type-id-4' && type !== 'type-id-7'
|
||||
);
|
||||
mockSavedObjects.getTypeRegistry.mockReturnValue(typeRegistryMock);
|
||||
|
||||
mockRetrieveClient = savedObjectsClientMock.create();
|
||||
|
@ -125,7 +128,7 @@ it('does not perform rotation if there are no Saved Objects to process', async (
|
|||
|
||||
expect(mockRetrieveClient.find).toHaveBeenCalledTimes(1);
|
||||
expect(mockRetrieveClient.find).toHaveBeenCalledWith({
|
||||
type: ['type-id-1', 'type-id-2', 'type-id-4', 'type-id-5'],
|
||||
type: ['type-id-1', 'type-id-2', 'type-id-4', 'type-id-5', 'type-id-7'],
|
||||
perPage: 12345,
|
||||
namespaces: ['*'],
|
||||
sortField: 'updated_at',
|
||||
|
@ -200,11 +203,12 @@ it('properly rotates encryption key', async () => {
|
|||
getMockSavedObject({ id: 'id-1' }),
|
||||
getMockSavedObject({ id: 'id-2', namespaces: ['ns-1'] }),
|
||||
getMockSavedObject({ id: 'id-4', namespaces: ['ns-2', 'ns-3'] }),
|
||||
getMockSavedObject({ id: 'id-7', namespaces: ['*'] }),
|
||||
];
|
||||
mockRetrieveClient.find.mockResolvedValue({
|
||||
total: 3,
|
||||
total: 4,
|
||||
saved_objects: savedObjects,
|
||||
per_page: 3,
|
||||
per_page: 4,
|
||||
page: 0,
|
||||
});
|
||||
mockUpdateClient.bulkUpdate.mockResolvedValue({
|
||||
|
@ -214,12 +218,12 @@ it('properly rotates encryption key', async () => {
|
|||
await expect(
|
||||
service.rotate(httpServerMock.createKibanaRequest(), { batchSize: 12345 })
|
||||
).resolves.toEqual({
|
||||
total: 3,
|
||||
successful: 3,
|
||||
total: 4,
|
||||
successful: 4,
|
||||
failed: 0,
|
||||
});
|
||||
|
||||
expect(mockEncryptionService.decryptAttributes).toHaveBeenCalledTimes(3);
|
||||
expect(mockEncryptionService.decryptAttributes).toHaveBeenCalledTimes(4);
|
||||
expect(mockEncryptionService.decryptAttributes).toHaveBeenCalledWith(
|
||||
{ type: 'type-id-1', id: 'id-1' },
|
||||
{ attr: 'attr-id-1' },
|
||||
|
@ -235,12 +239,18 @@ it('properly rotates encryption key', async () => {
|
|||
{ attr: 'attr-id-4' },
|
||||
{ omitPrimaryEncryptionKey: true }
|
||||
);
|
||||
expect(mockEncryptionService.decryptAttributes).toHaveBeenCalledWith(
|
||||
{ type: 'type-id-7', id: 'id-7' },
|
||||
{ attr: 'attr-id-7' },
|
||||
{ omitPrimaryEncryptionKey: true }
|
||||
);
|
||||
|
||||
expect(mockUpdateClient.bulkUpdate).toHaveBeenCalledTimes(1);
|
||||
expect(mockUpdateClient.bulkUpdate).toHaveBeenCalledWith([
|
||||
{ ...savedObjects[0], attributes: { attr: 'decrypted-attr-id-1' } },
|
||||
{ ...savedObjects[1], namespace: 'ns-1', attributes: { attr: 'decrypted-attr-id-2' } },
|
||||
{ ...savedObjects[2], namespace: 'ns-2', attributes: { attr: 'decrypted-attr-id-4' } },
|
||||
{ ...savedObjects[3], attributes: { attr: 'decrypted-attr-id-7' } },
|
||||
]);
|
||||
});
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@ import type {
|
|||
StartServicesAccessor,
|
||||
} from '@kbn/core/server';
|
||||
import { ENCRYPTION_EXTENSION_ID } from '@kbn/core-saved-objects-server';
|
||||
import { ALL_NAMESPACES_STRING } from '@kbn/core-saved-objects-utils-server';
|
||||
import type { AuthenticatedUser } from '@kbn/core-security-common';
|
||||
import type { PublicMethodsOf } from '@kbn/utility-types';
|
||||
|
||||
|
@ -258,11 +259,14 @@ export class EncryptionKeyRotationService {
|
|||
continue;
|
||||
}
|
||||
|
||||
const firstNamespace = savedObject.namespaces?.[0];
|
||||
|
||||
decryptedSavedObjects.push({
|
||||
...savedObject,
|
||||
attributes: decryptedAttributes,
|
||||
// `bulkUpdate` expects objects with a single `namespace`.
|
||||
namespace: savedObject.namespaces?.[0],
|
||||
// The optional object namespace for `bulkUpdate` is used to affect objects outside of the current space
|
||||
// '*' is an invalid option, and if the object exists in all spaces, we don't need to set the namespace
|
||||
namespace: firstNamespace !== ALL_NAMESPACES_STRING ? firstNamespace : undefined,
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
"@kbn/core-security-common",
|
||||
"@kbn/test-jest-helpers",
|
||||
"@kbn/config",
|
||||
"@kbn/core-saved-objects-utils-server",
|
||||
],
|
||||
"exclude": [
|
||||
"target/**/*",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue