Replace Encrypted Saved Object AAD exclude list with include list (#167705)

Closes #156023

## Summary

ESO = Encrypted Saved Object(s)

This PR modifies the `EncryptedSavedObjectTypeRegistration` definition,
replacing the `attributesToExcludeFromAAD` property with a
`attributesToIncludeInAAD` property. The purpose is to alter the default
inclusion of new SO attributes, which will help to resolve potential
decryption issues with serverless zero downtime upgrades (see
https://github.com/elastic/kibana/issues/156023).

NOTE: nested fields are included when the parent field is added to the
include list. In this way the include list behaves just as the exclude
list did.

#### Attention Code Owners: 
I attempted to create the include list for existing ESOs by comparing
the exclude list to the full list of attributes, ~~however, I am sure
this is either incomplete or partially incorrect~~ UPDATE: new tests
have been created to validate the include list (see the **Testing**
section). These changes will need to be carefully audited by the owning
teams during the review process. This PR will not merge until all code
owners have reviewed and approved the changes. If your team is a
consumer of ESOs, please see the **Testing** section below.

## Testing
Automated test suites have been updated to account for the changes to
ESO registration. The riskier part of this PR are the changes to
existing ESOs, and validating that they are effectively identical to
their previous implementations. I have used main branch Kibana to
generate several ESOs - one of each type, then saved those raw encrypted
objects to an esArchiver JSON file. New functional tests, in the
`encrypted_saved_objects_api_integration` suite, have been created to
verify that those objects can be successfully decrypted using the new
ESO definitions containing the AAD include list.

### ESO Types to Validate
See
`x-pack/test/encrypted_saved_objects_api_integration/tests/encrypted_saved_objects_aad_include_list.ts`

- [x] ACTION_SAVED_OBJECT_TYPE/'action'
- [x] ACTION_TASK_PARAMS_SAVED_OBJECT_TYPE/'action_task_params'
- [x] CONNECTOR_TOKEN_SAVED_OBJECT_TYPE/'connector_token'
- [x] RULE_SAVED_OBJECT_TYPE/'alert'
- [x] 'api_key_pending_invalidation'
- [x] OUTPUT_SAVED_OBJECT_TYPE/'ingest-outputs
- [x] MESSAGE_SIGNING_KEYS_SAVED_OBJECT_TYPE/'fleet-message-signing-keys
- [x] UNINSTALL_TOKENS_SAVED_OBJECT_TYPE/'fleet-uninstall-tokens'
- [x] syntheticsApiKeyObjectType/'uptime-synthetics-api-key'
- [x] syntheticsMonitorType/'synthetics-monitor'
- [x] syntheticsParamType/'synthetics-param'

### Flaky Test Runner
https://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/5419

---------

Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
Co-authored-by: Aleh Zasypkin <aleh.zasypkin@gmail.com>
This commit is contained in:
Jeramy Soucy 2024-03-08 11:15:02 -05:00 committed by GitHub
parent 8a594a4f96
commit f214e207e5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
23 changed files with 1095 additions and 177 deletions

12
.github/CODEOWNERS vendored
View file

@ -1584,6 +1584,18 @@ packages/react @elastic/appex-sharedux
/src/plugins/**/kibana.jsonc @elastic/kibana-core
/x-pack/plugins/**/kibana.jsonc @elastic/kibana-core
# Temporary Encrypted Saved Objects (ESO) guarding
# This additional code-ownership is meant to be a temporary precaution to notify the Kibana platform security team
# when an encrypted saved object is changed. Very careful review is necessary to ensure any changes are compatible
# with serverless zero downtime upgrades (ZDT). This section should be removed only when proper guidance for
# maintaining ESOs has been documented and consuming teams have acclimated to ZDT changes.
x-pack/plugins/actions/server/saved_objects/index.ts @elastic/response-ops @elastic/kibana-security
x-pack/plugins/alerting/server/saved_objects/index.ts @elastic/response-ops @elastic/kibana-security
x-pack/plugins/fleet/server/saved_objects/index.ts @elastic/fleet @elastic/kibana-security
x-pack/plugins/observability_solution/synthetics/server/saved_objects/saved_objects.ts @elastic/obs-ux-infra_services-team @elastic/kibana-security
x-pack/plugins/observability_solution/synthetics/server/saved_objects/synthetics_monitor.ts @elastic/obs-ux-infra_services-team @elastic/kibana-security
x-pack/plugins/observability_solution/synthetics/server/saved_objects/synthetics_param.ts @elastic/obs-ux-infra_services-team @elastic/kibana-security
####
## These rules are always last so they take ultimate priority over everything else
####

View file

@ -113,9 +113,10 @@ export class EsoModelVersionExample implements Plugin<void, void> {
aadField1: schema.maybe(schema.object({ flag1: schema.maybe(schema.boolean()) })),
secrets: schema.any(),
},
// 'ignore' will strip any new unknown fields coming from new versions (a zero-downtime upgrade consideration)
// We want to do this unless we have a compelling reason not to, like if we know we want to add a new AAD field
// in the next version (see model version 2)
// 'ignore' will strip any new unknown fields coming from new versions
// We want to do this unless we have a compelling reason not to, e.g. in a zero-downtime upgrade scenario
// and we know that in the next version we will add a new nested field to an attribute that is already
// included in AAD (see model version 2)
{ unknowns: 'ignore' }
),
create: schema.object({
@ -126,28 +127,36 @@ export class EsoModelVersionExample implements Plugin<void, void> {
}),
},
},
inputType: esoModelVersionExampleV1.EsoModelVersionExampleTypeRegistration, // Pass in the type registration for the specific version
outputType: esoModelVersionExampleV1.EsoModelVersionExampleTypeRegistration, // In this case both input an output are V1
// Pass in the type registration for the specific version. In this case both input and output are V1.
inputType: esoModelVersionExampleV1.EsoModelVersionExampleTypeRegistration,
outputType: esoModelVersionExampleV1.EsoModelVersionExampleTypeRegistration,
shouldTransformIfDecryptionFails: true,
}),
2: plugins.encryptedSavedObjects.createModelVersion({
modelVersion: {
changes: [
// Version 2 adds additional optional properties (or "sub-fields") to aadField1 and secrets, we're going to back fill them.
// Version 2 also adds an optional field aadExcludedField, which is excluded from AAD. This will be stripped out for
// older versions during zero-downtime upgrades due to the forwardCompatibility schema in model version 1.
// Version 2 adds a new attribute aadField2 which is included in AAD, we're not going to backfill it. During a
// zero-downtime upgrade, the previous version of Kibana will not know to include it in AAD based on the wrapped
// model version 1 definition. We need to keep it empty in this version, but can backfill it in the next version
// safely.
// Version 2 also adds an additional optional property (or "sub-field") to secrets, an encrypted field, we're
// going to backfill it. Note that during a zero-downtime upgrade, the previous version of Kibana needs to be
// able to handle additional properties to encrypted attributes gracefully if they are backfilled.
// Version 2 also adds an optional field aadExcludedField, which is excluded from AAD. This will be stripped out
// in older versions during zero-downtime upgrades due to the forwardCompatibility schema in model version 1.
{
type: 'data_backfill',
backfillFn: (doc) => {
const aadField1 = doc.attributes.aadField1;
const secrets = doc.attributes.secrets;
return {
attributes: {
aadField1: { ...aadField1, flag2: false },
secrets: {
...secrets,
b: "model version 2 adds property 'b' to the secrets attribute",
},
aadExcludedField: 'model version 2 adds this non-AAD attribute',
},
};
},
@ -161,17 +170,22 @@ export class EsoModelVersionExample implements Plugin<void, void> {
aadField1: schema.maybe(
schema.object({
flag1: schema.maybe(schema.boolean()),
flag2: schema.maybe(schema.boolean()),
})
),
aadField2: schema.maybe(
schema.object({
foo: schema.maybe(schema.string()),
bar: schema.maybe(schema.string()),
})
),
aadExcludedField: schema.maybe(schema.string()),
secrets: schema.any(),
},
// If we know that we will be adding a new AAD field in the next version, we will NOT strip new fields
// in the forward compatibility schema. This is a Zero-downtime upgrade consideration and ensures that
// old versions will use those fields when constructing AAD. The caveat is that we need to know ahead
// of time, and make sure the consuming code can handle having the additional attribute, even if it
// is not used yet.
// If we know that we will be adding a new property/sub-field to an AAD-included attribute in the next
// version, we will choose NOT to strip new fields in the forward compatibility schema. This is a zero-
// downtime upgrade consideration and ensures that old versions will use the additionally included
// fields when constructing AAD. The caveat is that we need to know ahead of time, and this version of
// Kibana (prior to version 3) needs to be able to handle the additional properties gracefully.
{ unknowns: 'allow' }
),
create: schema.object({
@ -180,7 +194,12 @@ export class EsoModelVersionExample implements Plugin<void, void> {
aadField1: schema.maybe(
schema.object({
flag1: schema.maybe(schema.boolean()),
flag2: schema.maybe(schema.boolean()),
})
),
aadField2: schema.maybe(
schema.object({
foo: schema.maybe(schema.string()),
bar: schema.maybe(schema.string()),
})
),
aadExcludedField: schema.maybe(schema.string()),
@ -188,20 +207,40 @@ export class EsoModelVersionExample implements Plugin<void, void> {
}),
},
},
inputType: esoModelVersionExampleV1.EsoModelVersionExampleTypeRegistration, // In this case we are expecting to transform from a V1 object
outputType: esoModelVersionExampleV2.EsoModelVersionExampleTypeRegistration, // to a V2 object
// In this case we are expecting to transform from a V1 object to a V2 object
inputType: esoModelVersionExampleV1.EsoModelVersionExampleTypeRegistration,
outputType: esoModelVersionExampleV2.EsoModelVersionExampleTypeRegistration,
shouldTransformIfDecryptionFails: true,
}),
3: plugins.encryptedSavedObjects.createModelVersion({
modelVersion: {
// Version 3 adds a new attribute aadField2 which is included in AAD, we're not going to back fill it.
// For zero-downtime this new attribute is ok, because the previous model version allows unknown fields and will not strip it.
// The previous version will include it by default when constructing AAD.
// Version 3 removes the toBeRemoved attribute.
// Version 3 adds an optional field flag2 to the aadField1 attribute, which is included in AAD. As the previous model
// version allows unknown fields, we can backill it here.
// Version 3 also backfills the previously added addField2, which is safe to do now, as the previous model version
// has already defined it as an AAD-included field.
changes: [
{
type: 'data_removal',
removedAttributePaths: ['toBeRemoved'],
},
{
type: 'data_backfill',
backfillFn: (doc) => {
const aadField1 = doc.attributes.aadField1;
return {
attributes: {
aadField1: { ...aadField1, flag2: false },
aadField2: {
foo: 'model version 3 backfills aadField2.foo',
bar: 'model version 3 backfills aadField2.bar',
},
},
};
},
},
],
schemas: {
forwardCompatibility: schema.object(
@ -244,8 +283,10 @@ export class EsoModelVersionExample implements Plugin<void, void> {
}),
},
},
inputType: esoModelVersionExampleV2.EsoModelVersionExampleTypeRegistration, // In this case we are expecting to transform from V2 to V3. This happens to be the latest
outputType: esoModelVersionExampleV3.EsoModelVersionExampleTypeRegistration, // version, but being explicit means we don't need to change this when we implement V4
// In this case we are expecting to transform from V2 to V3. This happens to be the latest
// version, but being explicit means we don't need to change this when we implement V4
inputType: esoModelVersionExampleV2.EsoModelVersionExampleTypeRegistration,
outputType: esoModelVersionExampleV3.EsoModelVersionExampleTypeRegistration,
shouldTransformIfDecryptionFails: true,
}),
},
@ -400,10 +441,10 @@ export class EsoModelVersionExample implements Plugin<void, void> {
const decrypted = await Promise.all(
documentVersionConstants.map(async (obj) => {
const parts = obj.id.split(':', 2);
const dooder = await esoClient.getDecryptedAsInternalUser(parts[0], parts[1], {
const result = await esoClient.getDecryptedAsInternalUser(parts[0], parts[1], {
namespace,
});
return dooder;
return result;
})
);

View file

@ -32,7 +32,7 @@ export interface EsoModelVersionExample {
export const EsoModelVersionExampleTypeRegistration: EncryptedSavedObjectTypeRegistration = {
type: EXAMPLE_SAVED_OBJECT_TYPE,
attributesToEncrypt: new Set(['secrets']),
attributesToExcludeFromAAD: new Set(['name', 'toBeRemoved']), // aadField1 is included in AAD, but not name or toBeRemoved
attributesToIncludeInAAD: new Set(['aadField1']), // aadField1 is included in AAD, but not name or toBeRemoved
};
// This is just some static information used to generate a document

View file

@ -10,10 +10,14 @@ import { EncryptedSavedObjectTypeRegistration } from '@kbn/encrypted-saved-objec
export const EXAMPLE_SAVED_OBJECT_TYPE = 'eso_model_version_example';
// V2 adds a new sub-field "flag2"
export interface EsoModelVersionExampleOptions1 {
flag1?: boolean;
flag2?: boolean;
}
// This is a new attribute added in V2
export interface EsoModelVersionExampleOptions2 {
foo?: string;
bar?: string;
}
// V2 adds a new encrypted sub-field "b"
@ -27,6 +31,7 @@ export interface EsoModelVersionExample {
name: string; // Display name attribute. Not part of AAD
toBeRemoved: string; // An attribute that will be removed in a later model version.
aadField1?: EsoModelVersionExampleOptions1; // Optional attribute that is part of AAD.
aadField2?: EsoModelVersionExampleOptions2; // Optional attribute that is part of AAD.
aadExcludedField?: string; // Optional attribute that is NOT part of AAD.
secrets: EsoModelVersionExampleSecretData; // An encrypted attribute.
}
@ -37,26 +42,29 @@ export interface EsoModelVersionExample {
export const EsoModelVersionExampleTypeRegistration: EncryptedSavedObjectTypeRegistration = {
type: EXAMPLE_SAVED_OBJECT_TYPE,
attributesToEncrypt: new Set(['secrets']),
attributesToExcludeFromAAD: new Set(['name', 'toBeRemoved', 'aadExcludedField']), // aadField1 is included in AAD, but not name, toBeRemoved, or aadExcludedField
attributesToIncludeInAAD: new Set(['aadField1']), // aadField1 is included in AAD, but not name, toBeRemoved, or aadExcludedField
};
// This is just some static information used to generate a document
// for this specific model version. Otherwise, creating a saved object
// will always create the latest model version.
// secrets: {
// a: 'this is a model version 2 object',
// b: 'this is a new nested encrypted field',
// }
export const ESO_MV_RAW_DOC = {
index: '.kibana',
id: 'eso_model_version_example:52868e00-7dd5-11ee-bc21-35484912189c',
id: 'eso_model_version_example:9495eb6f-e356-47f7-a5b9-dce395375cbe',
document: {
eso_model_version_example: {
name: 'MV2 Test',
toBeRemoved: 'nothing to see here',
aadField1: {
flag1: true,
flag2: true,
},
aadExcludedField: 'this will not be used in AAD',
aadExcludedField: 'this is a new field excluded from AAD',
secrets:
'uXBOQpvkI9+lAcfJ52yQAroKIIj+YBT9Ym3IpH1nmPBj2u51tZ07tnPQ3EtO379zHzGOMu+9Da3+bVmDbtsL0z/YrDad3f0o0XSnuEDvmPIVWqC0EwKguik+t63s5LrFvp4r+X3OmsG+jIISx/PXXgLl/8NiWa/urjp649lTGo/k4QvSHyQ4egeM1LjRihFSBFEZkQljF6SJLFocuDlQb8GHkVtgp0pKKfrZu0mI8Q==',
'yf9UJyLPfq5iN6oH1mhDQuGX9WRNUnbsN+YDsNDcbCHQAg4N9CC458s+3xwZG/Sm+0GO2o5v+JPUCbkBhuFCNybIdrrzWm00CwHscOYPA4yoLU3blyIMdMzRjClbkKyQNPANOVD7ST1xv5ZAqhujsFI3ascDEVCk+mfBIJbnwOPaohWvsLaMhc0igjpwAeadc8F0cGWg34Nkue0JoaUHxx8CdkComFnwQPK8BER+Wg==',
},
type: 'eso_model_version_example',
references: [],
@ -64,7 +72,7 @@ export const ESO_MV_RAW_DOC = {
namespaces: ['default'],
coreMigrationVersion: '8.8.0',
typeMigrationVersion: '10.2.0',
updated_at: '2023-11-08T01:22:46.112Z',
created_at: '2023-11-08T01:22:46.112Z',
updated_at: '2023-12-18T18:13:06.568Z',
created_at: '2023-12-18T18:13:06.568Z',
},
};

View file

@ -10,12 +10,12 @@ import { EncryptedSavedObjectTypeRegistration } from '@kbn/encrypted-saved-objec
export const EXAMPLE_SAVED_OBJECT_TYPE = 'eso_model_version_example';
// V3 adds an additional field 'flag2' to an AAD-included attribute
export interface EsoModelVersionExampleOptions1 {
flag1?: boolean;
flag2?: boolean;
}
// This is a new attribute added in V3
export interface EsoModelVersionExampleOptions2 {
foo?: string;
bar?: string;
@ -42,15 +42,19 @@ export interface EsoModelVersionExample {
export const EsoModelVersionExampleTypeRegistration: EncryptedSavedObjectTypeRegistration = {
type: EXAMPLE_SAVED_OBJECT_TYPE,
attributesToEncrypt: new Set(['secrets']),
attributesToExcludeFromAAD: new Set(['name', 'aadExcludedField']), // aadField1 and aadField2 are included in AAD, but not name, or aadExcludedField
attributesToIncludeInAAD: new Set(['aadField1', 'aadField2']), // aadField1 and aadField2 are included in AAD, but not name, or aadExcludedField
};
// This is just some static information used to generate a document
// for this specific model version. Otherwise, creating a saved object
// will always create the latest model version.
// secrets: {
// a: 'this is a model version 3 object',
// b: 'this property was added in model version 2',
// }
export const ESO_MV_RAW_DOC = {
index: '.kibana',
id: 'eso_model_version_example:4b43a8b0-7dd7-11ee-8355-7d13444c2fd7',
id: 'eso_model_version_example:bb379c40-8618-4747-9ace-9bb27209e57f',
document: {
eso_model_version_example: {
name: 'MV3 Test',
@ -59,12 +63,12 @@ export const ESO_MV_RAW_DOC = {
flag2: true,
},
aadField2: {
foo: 'bar',
bar: 'foo',
foo: 'theses were added in model version 2 and',
bar: 'are now safe to use in model version 3',
},
aadExcludedField: 'this is a field excluded from AAD',
secrets:
'YYtHdisdq44Mvd9VdUui62hM8OowEgkuWSfidWq11lG4aXYR61tf+G+BlbwO6rqKPbFWK238Vn1tP+zceeiCofDqEZkViinT1nGDGjArEEsmIUlDtj5IdaY6boMGRzUJ+37viUrISFXMVV9n2qVMp7IYb2BGkAb3hyh4+ZO9SPTbrKhkcpKgpLs3CEvmfsgeW/Tkxh+F65uK2RShkgLoPy62JI35XUz1paop+zSQ90yPL9ysoQ==',
'YQFEVfXtSDBWYjzzvFGC+LkReyAdDOFCyD9UoDdtL83HIFqH3VXb2TnankD4zaP0gralADCyuHOPohVmcBP5fYZ4mVW47aA1uDEP2ORHSGBXjUYNshiZ3+5JxDJRSmgIl0RD4mqAVNa7iQ7j2R+xvCyNG/OzFPw1hythAwv48JLUTXmOsvjJycqKqEkfhWbgvl4R2eZVHGAqsBZaB+J74E8KXGQXL7US+b+Xld/RUxTuhQqGlw==',
},
type: 'eso_model_version_example',
references: [],
@ -72,7 +76,7 @@ export const ESO_MV_RAW_DOC = {
namespaces: ['default'],
coreMigrationVersion: '8.8.0',
typeMigrationVersion: '10.3.0',
updated_at: '2023-11-08T01:36:52.923Z',
created_at: '2023-11-08T01:36:52.923Z',
updated_at: '2023-12-18T18:34:08.476Z',
created_at: '2023-12-18T18:34:08.476Z',
},
};

View file

@ -69,7 +69,7 @@ export function setupSavedObjects(
encryptedSavedObjects.registerType({
type: ACTION_SAVED_OBJECT_TYPE,
attributesToEncrypt: new Set(['secrets']),
attributesToExcludeFromAAD: new Set(['name']),
attributesToIncludeInAAD: new Set(['actionTypeId', 'isMissingSecrets', 'config']),
});
savedObjects.registerType({
@ -98,6 +98,14 @@ export function setupSavedObjects(
encryptedSavedObjects.registerType({
type: ACTION_TASK_PARAMS_SAVED_OBJECT_TYPE,
attributesToEncrypt: new Set(['apiKey']),
attributesToIncludeInAAD: new Set([
'actionId',
'consumer',
'params',
'executionId',
'relatedSavedObjects',
'source',
]),
});
savedObjects.registerType({
@ -114,5 +122,12 @@ export function setupSavedObjects(
encryptedSavedObjects.registerType({
type: CONNECTOR_TOKEN_SAVED_OBJECT_TYPE,
attributesToEncrypt: new Set(['token']),
attributesToIncludeInAAD: new Set([
'connectorId',
'tokenType',
'expiresAt',
'createdAt',
'updatedAt',
]),
});
}

View file

@ -33,45 +33,57 @@ import { ruleModelVersions } from './rule_model_versions';
export const RULE_SAVED_OBJECT_TYPE = 'alert';
// Use caution when removing items from this array! Any field which has
// ever existed in the rule SO must be included in this array to prevent
// decryption failures during migration.
export const RuleAttributesExcludedFromAAD = [
'scheduledTaskId',
'muteAll',
'mutedInstanceIds',
'updatedBy',
'updatedAt',
'executionStatus',
'monitoring',
'snoozeEndTime', // field removed in 8.2, but must be retained in case an rule created/updated in 8.2 is being migrated
'snoozeSchedule',
'isSnoozedUntil',
'lastRun',
'nextRun',
'revision',
'running',
export const RuleAttributesToEncrypt = ['apiKey'];
// Use caution when removing items from this array! These fields
// are used to construct decryption AAD and must be remain in
// this array to prevent decryption failures during migration.
// NOTE: Always update the RuleAttributesNotPartiallyUpdatable
// type if this const changes!
export const RuleAttributesIncludedInAAD = [
'enabled',
'name',
'tags',
'alertTypeId',
'consumer',
'legacyId',
'schedule',
'actions',
'params',
'mapped_params',
'createdBy',
'createdAt',
'apiKeyOwner',
'apiKeyCreatedByUser',
'throttle',
'notifyWhen',
'meta',
'alertDelay',
];
// useful for Pick<RawAlert, RuleAttributesExcludedFromAAD> which is a
// type which is a subset of RawAlert with just attributes excluded from AAD
// useful for Pick<RawAlert, RuleAttributesExcludedFromAAD>
export type RuleAttributesExcludedFromAADType =
| 'scheduledTaskId'
| 'muteAll'
| 'mutedInstanceIds'
| 'updatedBy'
| 'updatedAt'
| 'executionStatus'
| 'monitoring'
| 'snoozeEndTime'
| 'snoozeSchedule'
| 'isSnoozedUntil'
| 'lastRun'
| 'nextRun'
| 'revision'
| 'running';
// useful type for Omit<RuleAttributes, [...RuleAttributesToEncrypt, ...RuleAttributesIncludedInAAD]>
// which will produce a subset of RuleAttributes with just attributes that are safe to partually
// update from AAD
export type RuleAttributesNotPartiallyUpdatable =
| 'apiKey'
| 'enabled'
| 'name'
| 'tags'
| 'alertTypeId'
| 'consumer'
| 'legacyId'
| 'schedule'
| 'actions'
| 'params'
| 'mapped_params'
| 'createdBy'
| 'createdAt'
| 'apiKeyOwner'
| 'apiKeyCreatedByUser'
| 'throttle'
| 'notifyWhen'
| 'meta'
| 'alertDelay';
export function setupSavedObjects(
savedObjects: SavedObjectsServiceSetup,
@ -149,13 +161,14 @@ export function setupSavedObjects(
// Encrypted attributes
encryptedSavedObjects.registerType({
type: RULE_SAVED_OBJECT_TYPE,
attributesToEncrypt: new Set(['apiKey']),
attributesToExcludeFromAAD: new Set(RuleAttributesExcludedFromAAD),
attributesToEncrypt: new Set(RuleAttributesToEncrypt),
attributesToIncludeInAAD: new Set(RuleAttributesIncludedInAAD),
});
// Encrypted attributes
encryptedSavedObjects.registerType({
type: 'api_key_pending_invalidation',
attributesToEncrypt: new Set(['apiKeyId']),
attributesToIncludeInAAD: new Set(['createdAt']),
});
}

View file

@ -11,7 +11,7 @@ import {
SavedObjectsErrorHelpers,
} from '@kbn/core/server';
import { partiallyUpdateRule, PartiallyUpdateableRuleAttributes } from './partially_update_rule';
import { PartiallyUpdateableRuleAttributes, partiallyUpdateRule } from './partially_update_rule';
import { savedObjectsClientMock } from '@kbn/core/server/mocks';
import { RULE_SAVED_OBJECT_TYPE } from '.';
@ -39,14 +39,14 @@ describe('partially_update_rule', () => {
});
test('should work with extraneous attributes ', async () => {
const attributes = InvalidAttributes as unknown as PartiallyUpdateableRuleAttributes;
const attributes = ExtraneousAttributes as unknown as PartiallyUpdateableRuleAttributes;
soClient.update.mockResolvedValueOnce(MockUpdateValue);
await partiallyUpdateRule(soClient, MockRuleId, attributes);
expect(soClient.update).toHaveBeenCalledWith(
RULE_SAVED_OBJECT_TYPE,
MockRuleId,
DefaultAttributes,
ExtraneousAttributes,
{}
);
});
@ -124,7 +124,7 @@ const DefaultAttributes = {
updatedAt: '2019-02-12T21:01:22.479Z',
};
const InvalidAttributes = { ...DefaultAttributes, foo: 'bar' };
const ExtraneousAttributes = { ...DefaultAttributes, foo: 'bar' };
const MockRuleId = 'rule-id';

View file

@ -5,7 +5,7 @@
* 2.0.
*/
import { pick } from 'lodash';
import { omit, pick } from 'lodash';
import {
SavedObjectsClient,
SavedObjectsErrorHelpers,
@ -14,13 +14,18 @@ import {
import { RawRule } from '../types';
import {
RuleAttributesExcludedFromAAD,
RuleAttributesExcludedFromAADType,
RuleAttributesToEncrypt,
RuleAttributesIncludedInAAD,
RuleAttributesNotPartiallyUpdatable,
RULE_SAVED_OBJECT_TYPE,
} from '.';
import { RuleAttributes } from '../data/rule/types';
// We have calling code that references both RawRule and RuleAttributes,
// so we need to support both of these types (they are effectively the same)
export type PartiallyUpdateableRuleAttributes = Partial<
Pick<RawRule, RuleAttributesExcludedFromAADType>
| Omit<RawRule, RuleAttributesNotPartiallyUpdatable>
| Omit<RuleAttributes, RuleAttributesNotPartiallyUpdatable>
>;
interface PartiallyUpdateRuleSavedObjectOptions {
@ -41,9 +46,12 @@ export async function partiallyUpdateRule(
attributes: PartiallyUpdateableRuleAttributes,
options: PartiallyUpdateRuleSavedObjectOptions = {}
): Promise<void> {
// ensure we only have the valid attributes excluded from AAD
const attributeUpdates = pick(attributes, RuleAttributesExcludedFromAAD);
const updateOptions: SavedObjectsUpdateOptions<RawRule> = pick(
// ensure we only have the valid attributes that are not encrypted and are excluded from AAD
const attributeUpdates = omit(attributes, [
...RuleAttributesToEncrypt,
...RuleAttributesIncludedInAAD,
]);
const updateOptions: SavedObjectsUpdateOptions<RuleAttributes> = pick(
options,
'namespace',
'version',
@ -51,7 +59,7 @@ export async function partiallyUpdateRule(
);
try {
await savedObjectsClient.update<RawRule>(
await savedObjectsClient.update<RuleAttributes>(
RULE_SAVED_OBJECT_TYPE,
id,
attributeUpdates,

View file

@ -180,7 +180,7 @@ savedObjects.registerType({
});
```
### Example: Migating a Type
### Example: Migrating a Type
If your migration needs to change the type by, for example, removing an encrypted field, you will have to specify the legacy type for the input.
```typescript

View file

@ -15,7 +15,7 @@ it('correctly determines attribute properties', () => {
EncryptedSavedObjectTypeRegistration,
{
shouldBeEncrypted: boolean[];
shouldBeExcludedFromAAD: boolean[];
shouldBeIncludedInAAD: boolean[];
shouldBeStripped: boolean[];
}
]
@ -27,7 +27,7 @@ it('correctly determines attribute properties', () => {
},
{
shouldBeEncrypted: [true, true, true, true],
shouldBeExcludedFromAAD: [true, true, true, true],
shouldBeIncludedInAAD: [false, false, false, false],
shouldBeStripped: [true, true, true, true],
},
],
@ -38,7 +38,7 @@ it('correctly determines attribute properties', () => {
},
{
shouldBeEncrypted: [true, true, false, false],
shouldBeExcludedFromAAD: [true, true, false, false],
shouldBeIncludedInAAD: [false, false, false, false],
shouldBeStripped: [true, true, false, false],
},
],
@ -49,7 +49,7 @@ it('correctly determines attribute properties', () => {
},
{
shouldBeEncrypted: [true, true, false, false],
shouldBeExcludedFromAAD: [true, true, false, false],
shouldBeIncludedInAAD: [false, false, false, false],
shouldBeStripped: [true, true, false, false],
},
],
@ -57,11 +57,11 @@ it('correctly determines attribute properties', () => {
{
type: 'so-type',
attributesToEncrypt: new Set(['attr#1', 'attr#2']),
attributesToExcludeFromAAD: new Set(['attr#3']),
attributesToIncludeInAAD: new Set(['attr#4']),
},
{
shouldBeEncrypted: [true, true, false, false],
shouldBeExcludedFromAAD: [true, true, true, false],
shouldBeIncludedInAAD: [false, false, false, true],
shouldBeStripped: [true, true, false, false],
},
],
@ -73,11 +73,11 @@ it('correctly determines attribute properties', () => {
'attr#2',
{ key: 'attr#4', dangerouslyExposeValue: true },
]),
attributesToExcludeFromAAD: new Set(['attr#3']),
attributesToIncludeInAAD: new Set(['attr#3']),
},
{
shouldBeEncrypted: [true, true, false, true],
shouldBeExcludedFromAAD: [true, true, true, true],
shouldBeIncludedInAAD: [false, false, true, false], // will not include attr#4 because it is to be encrypted
shouldBeStripped: [true, true, false, false],
},
],
@ -89,11 +89,11 @@ it('correctly determines attribute properties', () => {
'attr#2',
{ key: 'attr#4', dangerouslyExposeValue: true },
]),
attributesToExcludeFromAAD: new Set(['some-other-attribute']),
attributesToIncludeInAAD: new Set(['attr#3', 'some-other-attribute']),
},
{
shouldBeEncrypted: [true, true, false, true],
shouldBeExcludedFromAAD: [true, true, false, true],
shouldBeIncludedInAAD: [false, false, true, false],
shouldBeStripped: [false, true, false, false],
},
],
@ -108,9 +108,25 @@ it('correctly determines attribute properties', () => {
expect(typeDefinition.shouldBeStripped(attributeName)).toBe(
asserts.shouldBeStripped[attributeIndex]
);
expect(typeDefinition.shouldBeExcludedFromAAD(attributeName)).toBe(
asserts.shouldBeExcludedFromAAD[attributeIndex]
expect(typeDefinition.shouldBeIncludedInAAD(attributeName)).toBe(
asserts.shouldBeIncludedInAAD[attributeIndex]
);
}
}
});
it('throws when the same attributes are included in AAD and encrypted', () => {
const registration = {
type: 'some-type',
attributesToEncrypt: new Set(['attr#1', 'attr#3', 'attr#5', 'attr#7']),
attributesToIncludeInAAD: new Set(['attr#1', 'attr#2', 'attr#4', 'attr#7']),
};
expect(() => {
new EncryptedSavedObjectAttributesDefinition(registration);
}).toThrow(
new Error(
`Invalid EncryptedSavedObjectTypeRegistration for type 'some-type'. attributesToIncludeInAAD must not contain any values in attributesToEncrypt: attr#1,attr#7`
)
);
});

View file

@ -9,15 +9,32 @@ import type { EncryptedSavedObjectTypeRegistration } from './encrypted_saved_obj
/**
* Represents the definition of the attributes of the specific saved object that are supposed to be
* encrypted. The definition also dictates which attributes should be excluded from AAD and/or
* encrypted. The definition also dictates which attributes should be included in AAD and/or
* stripped from response.
*/
export class EncryptedSavedObjectAttributesDefinition {
public readonly attributesToEncrypt: ReadonlySet<string>;
private readonly attributesToExcludeFromAAD: ReadonlySet<string> | undefined;
private readonly attributesToIncludeInAAD: ReadonlySet<string> | undefined;
private readonly attributesToStrip: ReadonlySet<string>;
constructor(typeRegistration: EncryptedSavedObjectTypeRegistration) {
if (typeRegistration.attributesToIncludeInAAD) {
const invalidAttributeKeys = new Array<string>();
typeRegistration.attributesToEncrypt.forEach((attribute) => {
const attributeKey = typeof attribute !== 'string' ? attribute.key : attribute;
if (typeRegistration.attributesToIncludeInAAD?.has(attributeKey)) {
invalidAttributeKeys.push(attributeKey);
}
});
if (invalidAttributeKeys.length > 0) {
throw new Error(
`Invalid EncryptedSavedObjectTypeRegistration for type '${typeRegistration.type}'. ` +
`attributesToIncludeInAAD must not contain any values in attributesToEncrypt: ${invalidAttributeKeys}`
);
}
}
const attributesToEncrypt = new Set<string>();
const attributesToStrip = new Set<string>();
for (const attribute of typeRegistration.attributesToEncrypt) {
@ -34,7 +51,7 @@ export class EncryptedSavedObjectAttributesDefinition {
this.attributesToEncrypt = attributesToEncrypt;
this.attributesToStrip = attributesToStrip;
this.attributesToExcludeFromAAD = typeRegistration.attributesToExcludeFromAAD;
this.attributesToIncludeInAAD = typeRegistration.attributesToIncludeInAAD;
}
/**
@ -47,14 +64,14 @@ export class EncryptedSavedObjectAttributesDefinition {
}
/**
* Determines whether particular attribute should be excluded from AAD.
* Determines whether particular attribute should be included in AAD.
* @param attributeName Name of the attribute.
*/
public shouldBeExcludedFromAAD(attributeName: string) {
public shouldBeIncludedInAAD(attributeName: string) {
return (
this.shouldBeEncrypted(attributeName) ||
(this.attributesToExcludeFromAAD != null &&
this.attributesToExcludeFromAAD.has(attributeName))
!this.shouldBeEncrypted(attributeName) &&
this.attributesToIncludeInAAD != null &&
this.attributesToIncludeInAAD.has(attributeName)
);
}
@ -65,4 +82,20 @@ export class EncryptedSavedObjectAttributesDefinition {
public shouldBeStripped(attributeName: string) {
return this.attributesToStrip.has(attributeName);
}
/**
* Collects all attributes (both keys and values) that should contribute to AAD.
* @param attributes Attributes of the saved object
*/
public collectAttributesForAAD(attributes: Record<string, unknown>) {
const aadAttributes: Record<string, unknown> = {};
if (this.attributesToIncludeInAAD) {
for (const attributeKey of this.attributesToIncludeInAAD) {
if (!this.shouldBeEncrypted(attributeKey) && Object.hasOwn(attributes, attributeKey)) {
aadAttributes[attributeKey] = attributes[attributeKey];
}
}
}
return aadAttributes;
}
}

View file

@ -508,6 +508,7 @@ describe('#encryptAttributes', () => {
service.registerType({
type: 'known-type-1',
attributesToEncrypt: new Set(['attrOne', 'attrThree', 'attrFour']),
attributesToIncludeInAAD: new Set(['attrTwo']),
});
const mockUser = mockAuthenticatedUser();
@ -535,6 +536,7 @@ describe('#encryptAttributes', () => {
service.registerType({
type: 'known-type-1',
attributesToEncrypt: new Set(['attrOne', 'attrThree', 'attrFour']),
attributesToIncludeInAAD: new Set(['attrTwo']),
});
await expect(
@ -556,6 +558,7 @@ describe('#encryptAttributes', () => {
service.registerType({
type: 'known-type-1',
attributesToEncrypt: new Set(['attrOne', 'attrThree']),
attributesToIncludeInAAD: new Set(['attrTwo']),
});
const mockUser = mockAuthenticatedUser();
@ -575,6 +578,7 @@ describe('#encryptAttributes', () => {
service.registerType({
type: 'known-type-1',
attributesToEncrypt: new Set(['attrOne', 'attrThree']),
attributesToIncludeInAAD: new Set(['attrTwo']),
});
const mockUser = mockAuthenticatedUser();
@ -590,18 +594,19 @@ describe('#encryptAttributes', () => {
});
});
it('does not include specified attributes to AAD', async () => {
it('only includes specified attributes in AAD', async () => {
const knownType1attributes = { attrOne: 'one', attrTwo: 'two', attrThree: 'three' };
service.registerType({
type: 'known-type-1',
attributesToEncrypt: new Set(['attrThree']),
attributesToIncludeInAAD: new Set(['attrOne', 'attrTwo']),
});
const knownType2attributes = { attrOne: 'one', attrTwo: 'two', attrThree: 'three' };
service.registerType({
type: 'known-type-2',
attributesToEncrypt: new Set(['attrThree']),
attributesToExcludeFromAAD: new Set(['attrTwo']),
attributesToIncludeInAAD: new Set(['attrOne']),
});
await expect(
@ -809,7 +814,7 @@ describe('#decryptAttributes', () => {
service.registerType({
type: 'known-type-1',
attributesToEncrypt: new Set(['attrThree']),
attributesToExcludeFromAAD: new Set(['attrOne']),
attributesToIncludeInAAD: new Set(['attrTwo']),
});
const encryptedAttributes = await service.encryptAttributes(
@ -912,6 +917,7 @@ describe('#decryptAttributes', () => {
service.registerType({
type: 'known-type-1',
attributesToEncrypt: new Set(['attrThree']),
attributesToIncludeInAAD: new Set(['attrOne', 'attrTwo']),
});
const encryptedAttributes = service.encryptAttributesSync(
@ -955,6 +961,7 @@ describe('#decryptAttributes', () => {
service.registerType({
type: 'known-type-1',
attributesToEncrypt: new Set(['attrThree']),
attributesToIncludeInAAD: new Set(['attrOne', 'attrTwo']),
});
const encryptedAttributes = service.encryptAttributesSync(
@ -998,6 +1005,7 @@ describe('#decryptAttributes', () => {
service.registerType({
type: 'known-type-1',
attributesToEncrypt: new Set(['attrThree']),
attributesToIncludeInAAD: new Set(['attrOne', 'attrTwo']),
});
const encryptedAttributes = service.encryptAttributesSync(
@ -1124,11 +1132,13 @@ describe('#decryptAttributes', () => {
service.registerType({
type: 'known-type-1',
attributesToEncrypt: new Set(['attrThree']),
attributesToIncludeInAAD: new Set(['attrOne', 'attrTwo']),
});
service.registerType({
type: 'known-type-2',
attributesToEncrypt: new Set(['attrThree']),
attributesToIncludeInAAD: new Set(['attrOne', 'attrTwo']),
});
encryptedAttributes = await service.encryptAttributes(
@ -1416,6 +1426,7 @@ describe('#decryptAttributes', () => {
service.registerType({
type: 'known-type-1',
attributesToEncrypt: new Set(['attrOne', 'attrThree', 'attrFour']),
attributesToIncludeInAAD: new Set(['attrTwo']),
});
const attributes = { attrOne: 'one', attrTwo: 'two', attrThree: 'three', attrFour: null };
@ -1476,6 +1487,7 @@ describe('#encryptAttributesSync', () => {
service.registerType({
type: 'known-type-1',
attributesToEncrypt: new Set(['attrOne', 'attrThree', 'attrFour']),
attributesToIncludeInAAD: new Set(['attrTwo']),
});
expect(
@ -1500,6 +1512,7 @@ describe('#encryptAttributesSync', () => {
service.registerType({
type: 'known-type-1',
attributesToEncrypt: new Set(['attrOne', 'attrThree', 'attrFour']),
attributesToIncludeInAAD: new Set(['attrTwo']),
});
expect(
@ -1521,6 +1534,7 @@ describe('#encryptAttributesSync', () => {
service.registerType({
type: 'known-type-1',
attributesToEncrypt: new Set(['attrOne', 'attrThree']),
attributesToIncludeInAAD: new Set(['attrTwo']),
});
expect(
@ -1537,6 +1551,7 @@ describe('#encryptAttributesSync', () => {
service.registerType({
type: 'known-type-1',
attributesToEncrypt: new Set(['attrOne', 'attrThree']),
attributesToIncludeInAAD: new Set(['attrTwo']),
});
expect(
@ -1555,13 +1570,14 @@ describe('#encryptAttributesSync', () => {
service.registerType({
type: 'known-type-1',
attributesToEncrypt: new Set(['attrThree']),
attributesToIncludeInAAD: new Set(['attrOne', 'attrTwo']),
});
const knownType2attributes = { attrOne: 'one', attrTwo: 'two', attrThree: 'three' };
service.registerType({
type: 'known-type-2',
attributesToEncrypt: new Set(['attrThree']),
attributesToExcludeFromAAD: new Set(['attrTwo']),
attributesToIncludeInAAD: new Set(['attrOne']),
});
expect(
@ -1747,7 +1763,7 @@ describe('#decryptAttributesSync', () => {
service.registerType({
type: 'known-type-1',
attributesToEncrypt: new Set(['attrThree']),
attributesToExcludeFromAAD: new Set(['attrOne']),
attributesToIncludeInAAD: new Set(['attrTwo']),
});
const encryptedAttributes = service.encryptAttributesSync(
@ -1846,6 +1862,7 @@ describe('#decryptAttributesSync', () => {
service.registerType({
type: 'known-type-1',
attributesToEncrypt: new Set(['attrThree']),
attributesToIncludeInAAD: new Set(['attrOne', 'attrTwo']),
});
const encryptedAttributes = service.encryptAttributesSync(
@ -1889,6 +1906,7 @@ describe('#decryptAttributesSync', () => {
service.registerType({
type: 'known-type-1',
attributesToEncrypt: new Set(['attrThree']),
attributesToIncludeInAAD: new Set(['attrOne', 'attrTwo']),
});
const encryptedAttributes = service.encryptAttributesSync(
@ -1932,6 +1950,7 @@ describe('#decryptAttributesSync', () => {
service.registerType({
type: 'known-type-1',
attributesToEncrypt: new Set(['attrThree']),
attributesToIncludeInAAD: new Set(['attrOne', 'attrTwo']),
});
const encryptedAttributes = service.encryptAttributesSync(
@ -2050,11 +2069,13 @@ describe('#decryptAttributesSync', () => {
const type1 = {
type: 'known-type-1',
attributesToEncrypt: new Set(['attrThree']),
attributesToIncludeInAAD: new Set(['attrOne', 'attrTwo']),
};
const type2 = {
type: 'known-type-2',
attributesToEncrypt: new Set(['attrThree']),
attributesToIncludeInAAD: new Set(['attrOne', 'attrTwo']),
};
beforeEach(() => {
@ -2356,6 +2377,7 @@ describe('#decryptAttributesSync', () => {
service.registerType({
type: 'known-type-1',
attributesToEncrypt: new Set(['attrOne', 'attrThree', 'attrFour']),
attributesToIncludeInAAD: new Set(['attrTwo']),
});
const attributes = { attrOne: 'one', attrTwo: 'two', attrThree: 'three', attrFour: null };

View file

@ -32,7 +32,7 @@ export interface AttributeToEncrypt {
export interface EncryptedSavedObjectTypeRegistration {
readonly type: string;
readonly attributesToEncrypt: ReadonlySet<string | AttributeToEncrypt>;
readonly attributesToExcludeFromAAD?: ReadonlySet<string>;
readonly attributesToIncludeInAAD?: ReadonlySet<string>;
}
/**
@ -600,12 +600,8 @@ export class EncryptedSavedObjectsService {
attributes: Record<string, unknown>
) {
// Collect all attributes (both keys and values) that should contribute to AAD.
const attributesAAD: Record<string, unknown> = {};
for (const [attributeKey, attributeValue] of Object.entries(attributes)) {
if (!typeDefinition.shouldBeExcludedFromAAD(attributeKey)) {
attributesAAD[attributeKey] = attributeValue;
}
}
const attributesAAD: Record<string, unknown> =
typeDefinition.collectAttributesForAAD(attributes);
if (Object.keys(attributesAAD).length === 0) {
this.options.logger.debug(
@ -615,6 +611,7 @@ export class EncryptedSavedObjectsService {
);
}
// Always add the descriptor to the AAD.
return stringify([...descriptorToArray(descriptor), attributesAAD]);
}

View file

@ -714,41 +714,13 @@ export function registerSavedObjects(savedObjects: SavedObjectsServiceSetup) {
});
}
export const OUTPUT_EXLCUDE_AAD_FIELDS = new Set([
'output_id',
'name',
'type',
'is_default',
'is_default_monitoring',
'hosts',
'ca_sha256',
'ca_trusted_fingerprint',
'config',
'config_yaml',
'is_internal',
'is_preconfigured',
'proxy_id',
'version',
'key',
'compression',
'compression_level',
'client_id',
'auth_type',
'connection_type',
'username',
'sasl',
'partition',
'random',
'round_robin',
'hash',
'topic',
'topics',
'headers',
'timeout',
'broker_timeout',
'required_acks',
'preset',
'secrets',
export const OUTPUT_INCLUDE_AAD_FIELDS = new Set([
'service_token',
'shipper',
'allow_edit',
'broker_ack_reliability',
'broker_buffer_size',
'channel_buffer_size',
]);
export const OUTPUT_ENCRYPTED_FIELDS = new Set([
@ -762,15 +734,17 @@ export function registerEncryptedSavedObjects(
encryptedSavedObjects.registerType({
type: OUTPUT_SAVED_OBJECT_TYPE,
attributesToEncrypt: OUTPUT_ENCRYPTED_FIELDS,
attributesToExcludeFromAAD: OUTPUT_EXLCUDE_AAD_FIELDS,
attributesToIncludeInAAD: OUTPUT_INCLUDE_AAD_FIELDS,
});
// Encrypted saved objects
encryptedSavedObjects.registerType({
type: MESSAGE_SIGNING_KEYS_SAVED_OBJECT_TYPE,
attributesToEncrypt: new Set(['passphrase']),
attributesToIncludeInAAD: new Set(['private_key', 'public_key', 'passphrase_plain']),
});
encryptedSavedObjects.registerType({
type: UNINSTALL_TOKENS_SAVED_OBJECT_TYPE,
attributesToEncrypt: new Set(['token']),
attributesToIncludeInAAD: new Set(['policy_id', 'token_plain']),
});
}

View file

@ -12,7 +12,7 @@ import type { Output } from '../../../common/types';
import {
getSavedObjectTypes,
OUTPUT_ENCRYPTED_FIELDS,
OUTPUT_EXLCUDE_AAD_FIELDS,
OUTPUT_INCLUDE_AAD_FIELDS,
} from '../../saved_objects';
import type { OutputSOAttributes } from '../../types';
@ -24,7 +24,7 @@ export const _getFieldsToIncludeEncryptedSO = once(() => {
for (const field of Object.keys(
getSavedObjectTypes()[OUTPUT_SAVED_OBJECT_TYPE].mappings.properties
)) {
if (!OUTPUT_EXLCUDE_AAD_FIELDS.has(field)) {
if (OUTPUT_INCLUDE_AAD_FIELDS.has(field)) {
res.add(field);
}
}

View file

@ -19,7 +19,6 @@ import {
import { PRIVATE_LOCATIONS_SAVED_OBJECT_TYPE } from './private_locations';
import { DYNAMIC_SETTINGS_DEFAULT_ATTRIBUTES } from '../constants/settings';
import { DynamicSettingsAttributes } from '../runtime_types/settings';
import { ConfigKey } from '../../common/runtime_types';
import { UptimeConfig } from '../../common/config';
import { settingsObjectId, settingsObjectType } from './uptime_settings';
import {
@ -46,7 +45,7 @@ export const registerUptimeSavedObjects = (
encryptedSavedObjects.registerType({
type: syntheticsServiceApiKey.name,
attributesToEncrypt: new Set(['apiKey']),
attributesToExcludeFromAAD: new Set([ConfigKey.ALERT_CONFIG]),
attributesToIncludeInAAD: new Set(['id', 'name']),
});
encryptedSavedObjects.registerType(SYNTHETICS_MONITOR_ENCRYPTED_TYPE);

View file

@ -13,7 +13,53 @@ import { syntheticsMonitorType } from '../../common/types/saved_objects';
import { ConfigKey, LegacyConfigKey, secretKeys } from '../../common/constants/monitor_management';
import { monitorMigrations } from './migrations/monitors';
const legacyConfigKeys = Object.values(LegacyConfigKey);
const attributesToIncludeInAAD = new Set([
ConfigKey.APM_SERVICE_NAME,
ConfigKey.CUSTOM_HEARTBEAT_ID,
ConfigKey.CONFIG_ID,
ConfigKey.CONFIG_HASH,
ConfigKey.ENABLED,
ConfigKey.FORM_MONITOR_TYPE,
ConfigKey.HOSTS,
ConfigKey.IGNORE_HTTPS_ERRORS,
ConfigKey.MONITOR_SOURCE_TYPE,
ConfigKey.JOURNEY_FILTERS_MATCH,
ConfigKey.JOURNEY_FILTERS_TAGS,
ConfigKey.JOURNEY_ID,
ConfigKey.MAX_REDIRECTS,
ConfigKey.MODE,
ConfigKey.MONITOR_TYPE,
ConfigKey.NAME,
ConfigKey.NAMESPACE,
ConfigKey.LOCATIONS,
ConfigKey.PLAYWRIGHT_OPTIONS,
ConfigKey.ORIGINAL_SPACE,
ConfigKey.PORT,
ConfigKey.PROXY_URL,
ConfigKey.PROXY_USE_LOCAL_RESOLVER,
ConfigKey.RESPONSE_BODY_INDEX,
ConfigKey.RESPONSE_HEADERS_INDEX,
ConfigKey.RESPONSE_BODY_MAX_BYTES,
ConfigKey.RESPONSE_STATUS_CHECK,
ConfigKey.REQUEST_METHOD_CHECK,
ConfigKey.REVISION,
ConfigKey.SCHEDULE,
ConfigKey.SCREENSHOTS,
ConfigKey.IPV4,
ConfigKey.IPV6,
ConfigKey.PROJECT_ID,
ConfigKey.TEXT_ASSERTION,
ConfigKey.TLS_CERTIFICATE_AUTHORITIES,
ConfigKey.TLS_CERTIFICATE,
ConfigKey.TLS_VERIFICATION_MODE,
ConfigKey.TLS_VERSION,
ConfigKey.TAGS,
ConfigKey.TIMEOUT,
ConfigKey.THROTTLING_CONFIG,
ConfigKey.URLS,
ConfigKey.WAIT,
ConfigKey.MONITOR_QUERY_ID,
]);
export const LEGACY_SYNTHETICS_MONITOR_ENCRYPTED_TYPE = {
type: syntheticsMonitorType,
@ -27,6 +73,25 @@ export const LEGACY_SYNTHETICS_MONITOR_ENCRYPTED_TYPE = {
* payload on the `secrets` key. This ensures performant decryption. */
...secretKeys,
]),
attributesToIncludeInAAD: new Set([
LegacyConfigKey.SOURCE_ZIP_URL,
LegacyConfigKey.SOURCE_ZIP_USERNAME,
LegacyConfigKey.SOURCE_ZIP_PASSWORD,
LegacyConfigKey.SOURCE_ZIP_FOLDER,
LegacyConfigKey.SOURCE_ZIP_PROXY_URL,
LegacyConfigKey.ZIP_URL_TLS_CERTIFICATE_AUTHORITIES,
LegacyConfigKey.ZIP_URL_TLS_CERTIFICATE,
LegacyConfigKey.ZIP_URL_TLS_KEY,
LegacyConfigKey.ZIP_URL_TLS_KEY_PASSPHRASE,
LegacyConfigKey.ZIP_URL_TLS_VERIFICATION_MODE,
LegacyConfigKey.ZIP_URL_TLS_VERSION,
LegacyConfigKey.THROTTLING_CONFIG,
LegacyConfigKey.IS_THROTTLING_ENABLED,
LegacyConfigKey.DOWNLOAD_SPEED,
LegacyConfigKey.UPLOAD_SPEED,
LegacyConfigKey.LATENCY,
...attributesToIncludeInAAD,
]),
};
export const SYNTHETICS_MONITOR_ENCRYPTED_TYPE = {
@ -41,12 +106,7 @@ export const SYNTHETICS_MONITOR_ENCRYPTED_TYPE = {
* payload on the `secrets` key. This ensures performant decryption. */
...secretKeys,
]),
attributesToExcludeFromAAD: new Set([
ConfigKey.ALERT_CONFIG,
ConfigKey.METADATA,
ConfigKey.MAX_ATTEMPTS,
...legacyConfigKeys,
]),
attributesToIncludeInAAD,
};
export const getSyntheticsMonitorSavedObjectType = (

View file

@ -10,6 +10,7 @@ import { syntheticsParamType } from '../../common/types/saved_objects';
export const SYNTHETICS_SECRET_ENCRYPTED_TYPE = {
type: syntheticsParamType,
attributesToEncrypt: new Set(['value']),
attributesToIncludeInAAD: new Set(['key', 'description', 'tags']),
};
export const syntheticsParamSavedObjectType: SavedObjectsType = {

View file

@ -0,0 +1,523 @@
{
"type": "doc",
"value": {
"id": "fleet-message-signing-keys:15d50edc-e8aa-4856-a5a3-df0188c4f550",
"index": ".kibana_ingest",
"source": {
"fleet-message-signing-keys": {
"private_key": "private_key",
"public_key": "public_key",
"passphrase": "+gMsqYHYbY62vUhjQbz6zVzTwfDC8n5dTkmFHoq7a6OwOvA8XIFfMYgThcnB/vewKbJWl/OCDrckZwuZCfpWjGGkgxUbUafO0FrkYz7vfxJs4tzHKWVpDhR+xio8YGKwoVofZzlGBMe9duAu6nj3BX7B92Q=",
"passphrase_plain": "This is the passphrase"
},
"type": "fleet-message-signing-keys",
"references": [],
"updated_at": "2023-12-21T22:10:37.254Z",
"namespaces": [
"default"
]
}
}
}
{
"type": "doc",
"value": {
"index": ".kibana_alerting_cases",
"id": "alert:5c892f54-c428-496b-b760-075c01b5fcdd",
"source": {
"alert": {
"name": "test_rule",
"tags": [],
"enabled": true,
"alertTypeId": "apm.transaction_error_rate",
"consumer": "alerts",
"legacyId": "some-legacy-id",
"schedule": {
"interval": "1m"
},
"alertDelay": {
"active": 1234
},
"actions": [
{
"group": "threshold_met",
"params": {
"documents": [
{
"it-worked": true
}
]
},
"actionTypeId": ".index",
"frequency": {
"summary": false,
"throttle": null,
"notifyWhen": "onActionGroupChange"
},
"uuid": "974b1bed-4eac-41af-b34a-d751b916d811",
"actionRef": "action_0"
}
],
"params": {
"threshold": 30,
"windowSize": 5,
"windowUnit": "m",
"environment": "ENVIRONMENT_ALL"
},
"mapped_params": {
"risk_score": 1.23,
"severity": "some-severity"
},
"createdBy": "elastic",
"updatedBy": "elastic",
"createdAt": "2024-01-16T22:09:07.964Z",
"updatedAt": "2024-01-16T22:09:07.964Z",
"apiKey": "RfC7Uqq/0mZ7iZou3FCKEtS697YRLrgx3RE/MH87s5RMYJf4KZXjDSsreTJfMIxq2LrStpx9DnYaNSQ4HTWholYqwwheYdnC9+lXWI42aKJat3AibhlpCIeMt1YHZrO+9pqjoAKJE9xtIPA1CPN+5oGTFsgcbe+lb3qCBjx1/jbeBhq7x6vRw0FnALBk58QUdWemYeBH1LRURk/yydk=",
"apiKeyOwner": "elastic",
"apiKeyCreatedByUser": false,
"throttle": "some-throttle-value",
"notifyWhen": "onThrottleInterval",
"muteAll": false,
"mutedInstanceIds": [],
"executionStatus": {
"status": "ok",
"lastExecutionDate": "2024-01-16T22:09:09.482Z",
"lastDuration": 1651,
"error": null,
"warning": null
},
"monitoring": {
"run": {
"history": [
{
"success": true,
"timestamp": 1705442949482,
"duration": 1651
}
],
"calculated_metrics": {
"success_ratio": 1,
"p50": 1651,
"p95": 1651,
"p99": 1651
},
"last_run": {
"timestamp": "2024-01-16T22:09:09.482Z",
"metrics": {
"duration": 1651,
"total_search_duration_ms": null,
"total_indexing_duration_ms": null,
"total_alerts_detected": null,
"total_alerts_created": null,
"gap_duration_s": null
}
}
}
},
"snoozeSchedule": [],
"revision": 0,
"running": false,
"meta": {
"versionApiKeyLastmodified": "8.13.0"
},
"scheduledTaskId": "903b890f-4e98-4ea5-bc10-71433f01de18",
"lastRun": {
"outcome": "succeeded",
"outcomeOrder": 0,
"outcomeMsg": null,
"warning": null,
"alertsCount": {
"active": 0,
"new": 0,
"recovered": 0,
"ignored": 0
}
},
"nextRun": "2024-01-16T22:10:09.409Z"
},
"type": "alert",
"references": [],
"managed": false,
"namespaces": [
"default"
],
"coreMigrationVersion": "8.8.0",
"typeMigrationVersion": "10.1.0",
"updated_at": "2024-02-22T19:34:13.564Z",
"created_at": "2024-02-22T19:34:13.564Z"
}
}
}
{
"type": "doc",
"value": {
"index": ".kibana_alerting_cases",
"id": "action:1e78caea-c2d9-47ec-bba5-5b69c211c274",
"source": {
"action": {
"actionTypeId": ".torq",
"name": "test-torq-connector",
"isMissingSecrets": false,
"config": {
"webhookIntegrationUrl": "https://hooks.torq.io/v1/hookid"
},
"secrets": "3BUB4WqB0+RM/ty2XwJLppQWl9o0rzbmdpGGzzMBhWYSM+4uzVesnULPQjHz7mP8EQDrveFip/J+1kdcbQ5OqgdbEpnZt6luK43HJfT3c0A3t9n6V3mkS6o7lLMag41Gs9AJEYLKD140zRdECHkzsXkGIwFBdY6QR0rFt8r95Q=="
},
"type": "action",
"references": [],
"managed": false,
"namespaces": [
"default"
],
"coreMigrationVersion": "8.8.0",
"typeMigrationVersion": "8.3.0",
"updated_at": "2024-01-17T00:04:47.371Z",
"created_at": "2024-01-17T00:04:47.371Z"
}
}
}
{
"type": "doc",
"value": {
"index": ".kibana",
"id": "synthetics-param:a304e397-723a-495c-a934-126504d53d10",
"source": {
"synthetics-param": {
"key": "test_synthetics_param",
"value": "VxVUb6Ic61EO/VYkgTrnhK7YO2aOeJdEjPowQ0SJd0W2S43VdNL+gx1Um5sqi2YaAk1ArP/tJ2Bfdxq4I6u64O5l6p2LHio6RgCb0QwzK7fmY0pS4iRYiPyhWmrZn0Y0qkVonQXsux+nAZWJQv5JZZVm/loAc0jA0kmVTNFUE/K0sjrFnASqH9OAr5Z+kzgGCPkMK4HmP79N8H23",
"description": "some description here",
"tags": [
"a",
"b",
"c"
]
},
"type": "synthetics-param",
"references": [],
"managed": false,
"namespaces": [
"default"
],
"coreMigrationVersion": "8.8.0",
"updated_at": "2024-01-18T22:32:44.274Z",
"created_at": "2024-01-18T22:32:44.274Z"
}
}
}
{
"type": "doc",
"value": {
"index": ".kibana",
"id": "synthetics-monitor:305ea066-0f33-47f2-8d15-2bf82c5ea430",
"source": {
"synthetics-monitor": {
"type": "browser",
"form_monitor_type": "multistep",
"enabled": true,
"alert": {
"status": {
"enabled": true
},
"tls": {
"enabled": true
}
},
"schedule": {
"unit": "m",
"number": "10"
},
"service.name": "some-service-name",
"custom_heartbeat_id": "some-heartbeat-id",
"config_id": "85e2583e-d02e-4cff-8b23-a91ae75e8dc2",
"tags": [
"a",
"b",
"c"
],
"timeout": 9999,
"wait": 200,
"name": "localhost",
"locations": [
{
"id": "b9ba5cfb-a676-4e3e-ab74-316ae2d697de",
"label": "test_location",
"geo": {
"lat": 0,
"lon": 0
},
"isServiceManaged": false
}
],
"namespace": "default",
"original_space": "some-space",
"origin": "ui",
"journey_id": "some-journey-id",
"hash": "some-hash",
"max_redirects": 2,
"mode": "some-mode",
"id": "85e2583e-d02e-4cff-8b23-a91ae75e8dc2",
"max_attempts": 2,
"project_id": "some-project-id",
"playwright_options": "some-playwright-options",
"__ui": {
"script_source": {
"is_generated_script": false,
"file_name": ""
}
},
"url.port": 9555,
"proxy_url": "some-proxy-url",
"proxy_use_local_resolver": false,
"response.include_body": false,
"response.include_headers": false,
"response.include_body_max_bytes": true,
"check.response.status": 200,
"check.request.method": "get",
"ipv4": "10.10.10.10",
"ipv6": "2001:0db8:85a3:0000:0000:8a2e:0370:7334",
"playwright_text_assertion": "some-assertion",
"urls": "localhost",
"screenshots": "on",
"filter_journeys.match": "some-filter-journeys-match",
"filter_journeys.tags": [
"a",
"b",
"c"
],
"ignore_https_errors": false,
"throttling": {
"value": {
"download": "5",
"upload": "3",
"latency": "20"
},
"id": "default",
"label": "Default"
},
"ssl.certificate_authorities": "some-ca",
"ssl.certificate": "some-certificate",
"ssl.verification_mode": "full",
"ssl.supported_protocols": [
"TLSv1.1",
"TLSv1.2",
"TLSv1.3"
],
"revision": 2,
"secrets": "hK+lf2dby84QJJm2W9Sml+XdqtzXUZGAcMlRNBP8ta+WjkCRqBc8e2IZaHCVwQsp4YhjPuP2DANk5fbge0fcjR/1BPpenpcRPvglSxTyfcrIYH8LtZ6Jt4Z6D1126101CdDMwtFHVVZhx5g26b4FSd2lr8mtjCda+K6tFbKTk8qhnEx0f+L6PhlFBAYruehfQd6NJFd/Ts4CKk4tHONhpCsGFHL32HUipGgxiDJi3oLMA4+QGAAd/BcsONC6Fh6niRUi7IbCkTxqJgOCHrYtOfuDW8K+0UF2ysgGuhT+fKi7ldL26KI3s2qc60GbQ6+KhUunnTwpE7PpZjqmELxHBusq4p+Q/zjK4IUBNFEvFJihA9AraCRqtSI6ckho1qpy0ny/TX7H2hyYibgiFyxW0OeHd13Y195vs0DArHik+BafZGnXfx+S2m/GYVGnTSY8X1VevhWeEuMzOE7OC703uGwzUns3/fmGwqQwk/7xnnU6slIcYIP38yqDBbnuFdTyREumNA=="
},
"type": "synthetics-monitor",
"references": [],
"managed": false,
"coreMigrationVersion": "8.8.0",
"typeMigrationVersion": "8.9.0",
"updated_at": "2024-01-19T00:13:32.522Z",
"created_at": "2024-01-19T00:13:32.522Z"
}
}
}
{
"type": "doc",
"value": {
"index": ".kibana_alerting_cases",
"id": "connector_token:601750d9-a82a-4344-ac5c-badfbe12a4ca",
"source": {
"connector_token": {
"connectorId": "connector-id",
"token": "ToodRP10TblHckqc9mIGKv4hWUN/rjhzP2OW4j41/z4V4HLSMGJGd6LknngRK7il+8nvMh2pvWJCA/6iIdM62+GAqJZlaHy4gOJnxb62J8ucolZxmmSX2AL1Fsn+6BaIVtegxDt82fdm4S94ANlpwzt9JvshB20vm32vkZrDl74/E9pwtHSsyz+b6s2iyLfELoo2feo=",
"expiresAt": "9999",
"tokenType": "access_token",
"createdAt": "2024-01-17T16:27:21.181Z",
"updatedAt": "2024-01-17T16:27:21.181Z"
},
"type": "connector_token",
"references": [],
"managed": false,
"coreMigrationVersion": "8.8.0",
"updated_at": "2024-01-17T16:27:21.181Z",
"created_at": "2024-01-17T16:27:21.181Z"
}
}
}
{
"type": "doc",
"value": {
"index": ".kibana_ingest",
"id": "ingest-outputs:e7e5ea1b-8342-45f3-8051-1bc1448fd923",
"source": {
"ingest-outputs": {
"service_token": "some-service-token",
"shipper": {
"prop1": "a",
"prop2": "b"
},
"allow_edit": false,
"broker_ack_reliability": "some-value",
"broker_buffer_size": 100,
"channel_buffer_size": 100,
"name": "test_kafka_output",
"type": "kafka",
"hosts": [
"localhost:9092"
],
"is_default": false,
"is_default_monitoring": false,
"config_yaml": "",
"proxy_id": null,
"client_id": "Elastic",
"version": "1.0.0",
"key": "some-key",
"compression": "none",
"auth_type": "user_pass",
"username": "some-username",
"sasl": {
"mechanism": "PLAIN"
},
"partition": "random",
"random": {
"group_events": 1
},
"topics": [
{
"topic": "some topic"
}
],
"headers": [
{
"key": "",
"value": ""
}
],
"timeout": 30,
"broker_timeout": 30,
"required_acks": 1,
"ssl": "QxjwNqSXWYqkJmZkgn9o4jBvzu6JIvqatP2F+CH3zPSF9fLSLBp7Q6PRWjztmuKv0Uq6Q+BrZRM4rE7OTt2wQoYrhtRETzQsxWWXXDD9B2Ph7ivUqlRiZmXLSERNYDPYlWspRDlv8e5gcncV8WUyinJAnRucNZuFQBvH1H+L7CkwpuVOG8avoiaJ00ZEn93AruSMxvPdTDfd+YIOjrcrE/ReU4fxith794adlLDDubN5vblbvBM0O4fsGjAoYxjNpWdOm9GvoxTDDw9v1QM9FLwV2i1FAYSVXLc69OWnd5s=",
"password": "xtsCxu6mdPQxLusMO6pkfM3yriLyt+8/9pn3BHcc5qce4wYjrdWhRsyyMcMSR/DUwfZzmWXDNT3uYUxeegbMzDwypqCcgYyGl85jB6ekUOJeGkokfW0vl130Xk7TccY6wjlyzMVC61PWavNLfaaEOEdGktKcx+6Tn+syxAc734puI0wMGxgyenMX+lkXqXEW4TYZ27hOthWwKSW35V/m"
},
"type": "ingest-outputs",
"references": [],
"managed": false,
"coreMigrationVersion": "8.8.0",
"typeMigrationVersion": "10.4.0",
"updated_at": "2024-01-23T03:10:45.098Z",
"created_at": "2024-01-23T03:10:45.098Z"
}
}
}
{
"type": "doc",
"value": {
"index": ".kibana_ingest",
"id": "fleet-uninstall-tokens:1db22a8f-4cbb-415d-90d1-7608a6923ba1",
"source": {
"fleet-uninstall-tokens": {
"policy_id": "some-policy-id",
"token": "eIGQf+EuprdGJlLunhXdvPoDO1NCLG3RtsbhVjWLbRuG+/YmNa8GFyD6924nwNJOND8900YQjT1sGInpQI3rLTC6KX0fthjlBk2RWElyndlnE6jga140dGNf++cbrY1d2c1k6JYPb+SjGs9VEfHQlrM9RpAFx8ZRUGHEbzgGCsaGVcNUZdB0G4A0UFrLSkcme2ICNgxp/qS0xU+0",
"token_plain": "This token was encrypted prior to the AAD include list change."
},
"type": "fleet-uninstall-tokens",
"references": [],
"managed": false,
"coreMigrationVersion": "8.8.0",
"updated_at": "2024-01-17T18:19:50.628Z",
"created_at": "2024-01-17T18:19:50.628Z"
}
}
}
{
"type": "doc",
"value": {
"index": ".kibana",
"id": "uptime-synthetics-api-key:b448dffe-6e9c-4032-aa9a-5d9446531b83",
"source": {
"uptime-synthetics-api-key": {
"id": "some-id",
"name": "some-name",
"apiKey": "iqDazOl7MJ5ZAMFGm5p8palEp72ZYIEdNVfmd+WPVfpak61HiOZZx3HmeNaFZa6yviAWm+ES6e2PNKAOPRKmvPWbnQfSXI2Eww2zC+st7RrzNEEGiU7j2w4h+tgCfvGco8CiQZIGZ9dhViOW32p6nsKiuo8P9dRIEdYJA7zPU3oilA4oAz87DTAO3rmdn2dCQqqyOUuEDPL+ytpCyMY="
},
"type": "uptime-synthetics-api-key",
"references": [],
"managed": false,
"coreMigrationVersion": "8.8.0",
"updated_at": "2024-01-17T22:27:14.580Z",
"created_at": "2024-01-17T22:27:14.580Z"
}
}
}
{
"type": "doc",
"value": {
"index": ".kibana_alerting_cases",
"id": "api_key_pending_invalidation:e67824d0-b428-47c7-be46-fd4e2021519f",
"source": {
"api_key_pending_invalidation": {
"createdAt": "2024-02-08T19:10:37.114Z",
"apiKeyId": "QXdPIxu0RSFEr2g0n0F/adxDeiu5hR4+CAnX/OvW59oJwTs3knvVxnPttXfcNR8V2fE3btEN5wwXh+fgZ+ADZ3E/fbFxz1Op8tToYWlSheZX3RennPgfnfi5f99edg0FWtAf0TU2FlBnAqzmDrxzhA+q6m3mj4/hzZKuJI2ghuECWJFj0wMLQ6ZDdCLOdRrX5MU7Enxtqvue7uZGboJxEoA="
},
"type": "api_key_pending_invalidation",
"references": [],
"managed": false,
"coreMigrationVersion": "8.8.0",
"updated_at": "2024-02-08T19:10:37.114Z",
"created_at": "2024-02-08T19:10:37.114Z"
}
}
}
{
"type": "doc",
"value": {
"index": ".kibana_alerting_cases",
"id": "action_task_params:248408b1-b3b0-46a0-80fa-e2b6fbc9085d",
"source": {
"action_task_params": {
"apiKey": "UI4MwCfF2yw1h+JQMEEFu18HI0ebFrGxbgKFA76YHKLOdpG9PY4oG5h+ctJjVlyUtcv8+DGnnvpjmxWErJxSe9ni8aQn9cBEZAqScOt1V7eZ5JB2fUVQxFIkrTmOG20vih1OSjaZl4tm55vziSXnvnQapksFWaQGQuIp1c2EasBESHrx9FTgpnUR7UvULYx3eNeQHIvenB030c8gDwg=",
"actionId": "some-action-id",
"consumer": "some-consumer",
"params": {
"param1": "a",
"param2": "b",
"param3": "c"
},
"executionId": "some-exe-id",
"relatedSavedObjects": [
{
"namespace": "default",
"id": "obj1-id",
"type": "obj1-type"
},
{
"namespace": "default",
"id": "obj2-id",
"type": "obj2-type",
"typeId": "some-type-id"
},
{
"namespace": "default",
"id": "obj3-id",
"type": "obj3-type"
}
],
"source": "SAVED_OBJECT"
},
"type": "action_task_params",
"references": [],
"managed": false,
"namespaces": [
"default"
],
"coreMigrationVersion": "8.8.0",
"typeMigrationVersion": "8.0.0",
"updated_at": "2024-02-08T15:31:13.593Z",
"created_at": "2024-02-08T15:31:13.593Z"
}
}
}

View file

@ -78,7 +78,7 @@ export const plugin: PluginInitializer<void, void, PluginsSetup, PluginsStart> =
'privateProperty',
{ key: 'publicPropertyStoredEncrypted', dangerouslyExposeValue: true },
]),
attributesToExcludeFromAAD: new Set(['publicPropertyExcludedFromAAD']),
attributesToIncludeInAAD: new Set(['publicProperty']),
});
}
@ -164,12 +164,14 @@ function defineTypeWithMigration(core: CoreSetup<PluginsStart>, deps: PluginsSet
const typePriorTo790 = {
type: SAVED_OBJECT_WITH_MIGRATION_TYPE,
attributesToEncrypt: new Set(['encryptedAttribute']),
attributesToIncludeInAAD: new Set(['nonEncryptedAttribute']), // No attributes were excluded previously, so we have to add this
};
// current type is registered
deps.encryptedSavedObjects.registerType({
type: SAVED_OBJECT_WITH_MIGRATION_TYPE,
attributesToEncrypt: new Set(['encryptedAttribute', 'additionalEncryptedAttribute']),
attributesToIncludeInAAD: new Set(['nonEncryptedAttribute']), // No attributes were excluded previously, so we have to add this
});
core.savedObjects.registerType({
@ -261,11 +263,13 @@ function defineModelVersionWithMigration(core: CoreSetup<PluginsStart>, deps: Pl
const typePriorTo810 = {
type: SAVED_OBJECT_MV_TYPE,
attributesToEncrypt: new Set(['encryptedAttribute']),
attributesToIncludeInAAD: new Set(['nonEncryptedAttribute']),
};
const latestType = {
type: SAVED_OBJECT_MV_TYPE,
attributesToEncrypt: new Set(['encryptedAttribute', 'additionalEncryptedAttribute']),
attributesToIncludeInAAD: new Set(['nonEncryptedAttribute']),
};
deps.encryptedSavedObjects.registerType(latestType);

View file

@ -0,0 +1,187 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { MAIN_SAVED_OBJECT_INDEX } from '@kbn/core-saved-objects-server';
import expect from '@kbn/expect';
import { FtrProviderContext } from '../ftr_provider_context';
export default function ({ getService }: FtrProviderContext) {
const supertest = getService('supertest');
const esArchiver = getService('esArchiver');
const es = getService('es');
describe('encrypted saved objects decryption', () => {
// This test uses esArchiver to load snapshots of encrypted objects which were captured from the main branch prior to
// the AAD include list change. It then uses get-decrypted-as-internal-user to attempt to decrypt each encrypted object.
describe('aad exclude list to include list', () => {
before(async () => {
await es.indices.putMapping({ index: MAIN_SAVED_OBJECT_INDEX, dynamic: true });
await esArchiver.load(
'x-pack/test/encrypted_saved_objects_api_integration/fixtures/es_archiver/encrypted_saved_objects_pre_aad_change'
);
});
after(async () => {
await esArchiver.unload(
'x-pack/test/encrypted_saved_objects_api_integration/fixtures/es_archiver/encrypted_saved_objects_pre_aad_change'
);
});
it(`successfully decrypts 'action' objects`, async () => {
const decryptResponse = await supertest
.get(
`/api/hidden_saved_objects/get-decrypted-as-internal-user/action/1e78caea-c2d9-47ec-bba5-5b69c211c274`
)
.expect(200);
const decryptedObject = JSON.parse(decryptResponse.text);
expect(decryptedObject.attributes.secrets).to.eql({ token: 'some-random-token-value' });
});
it(`successfully decrypts 'action_task_params' objects`, async () => {
const decryptResponse = await supertest
.get(
`/api/hidden_saved_objects/get-decrypted-as-internal-user/action_task_params/248408b1-b3b0-46a0-80fa-e2b6fbc9085d`
)
.expect(200);
const decryptedObject = JSON.parse(decryptResponse.text);
expect(decryptedObject.attributes.apiKey).to.eql(
'This api key was encrypted prior to the AAD include list change.'
);
});
it(`successfully decrypts 'connector_token' objects`, async () => {
const decryptResponse = await supertest
.get(
`/api/hidden_saved_objects/get-decrypted-as-internal-user/connector_token/601750d9-a82a-4344-ac5c-badfbe12a4ca`
)
.expect(200);
const decryptedObject = JSON.parse(decryptResponse.text);
expect(decryptedObject.attributes.token).to.eql(
'Token value encrypted prior to AAD include list change.'
);
});
it(`successfully decrypts 'alert' objects`, async () => {
const decryptResponse = await supertest
.get(
`/api/hidden_saved_objects/get-decrypted-as-internal-user/alert/5c892f54-c428-496b-b760-075c01b5fcdd`
)
.expect(200);
const decryptedObject = JSON.parse(decryptResponse.text);
expect(decryptedObject.attributes.apiKey).to.eql(
'This api key was encrypted prior to the AAD include list change.'
);
});
it(`successfully decrypts 'api_key_pending_invalidation' objects`, async () => {
const decryptResponse = await supertest
.get(
`/api/hidden_saved_objects/get-decrypted-as-internal-user/api_key_pending_invalidation/e67824d0-b428-47c7-be46-fd4e2021519f`
)
.expect(200);
const decryptedObject = JSON.parse(decryptResponse.text);
expect(decryptedObject.attributes.apiKeyId).to.eql(
'This api key id was encrypted prior to the AAD include list change.'
);
});
it(`successfully decrypts 'ingest-outputs' objects`, async () => {
const decryptResponse = await supertest
.get(
`/api/hidden_saved_objects/get-decrypted-as-internal-user/ingest-outputs/e7e5ea1b-8342-45f3-8051-1bc1448fd923`
)
.expect(200);
const decryptedObject = JSON.parse(decryptResponse.text);
const ssl = JSON.parse(decryptedObject.attributes.ssl);
expect(ssl).to.eql({
certificate_authorities: [
'This SSL CA was encrypted prior to the AAD include list change.',
],
verification_mode: 'full',
});
expect(decryptedObject.attributes.password).to.eql(
'This password was encrypted prior to the AAD include list change.'
);
});
it(`successfully decrypts 'fleet-message-signing-keys' objects`, async () => {
const decryptResponse = await supertest
.get(
`/api/hidden_saved_objects/get-decrypted-as-internal-user/fleet-message-signing-keys/15d50edc-e8aa-4856-a5a3-df0188c4f550`
)
.expect(200);
const decryptedObject = JSON.parse(decryptResponse.text);
expect(decryptedObject.attributes.passphrase).to.eql('This is the passphrase');
});
it(`successfully decrypts 'fleet-uninstall-tokens' objects`, async () => {
const decryptResponse = await supertest
.get(
`/api/hidden_saved_objects/get-decrypted-as-internal-user/fleet-uninstall-tokens/1db22a8f-4cbb-415d-90d1-7608a6923ba1`
)
.expect(200);
const decryptedObject = JSON.parse(decryptResponse.text);
expect(decryptedObject.attributes.token).to.eql(
'This token was encrypted prior to the AAD include list change.'
);
});
it(`successfully decrypts 'uptime-synthetics-api-key' objects`, async () => {
const decryptResponse = await supertest
.get(
`/api/hidden_saved_objects/get-decrypted-as-internal-user/uptime-synthetics-api-key/b448dffe-6e9c-4032-aa9a-5d9446531b83`
)
.expect(200);
const decryptedObject = JSON.parse(decryptResponse.text);
expect(decryptedObject.attributes.apiKey).to.eql(
'This api key was encrypted prior to the AAD include list change.'
);
});
it(`successfully decrypts 'synthetics-monitor' objects`, async () => {
const decryptResponse = await supertest
.get(
`/api/hidden_saved_objects/get-decrypted-as-internal-user/synthetics-monitor/305ea066-0f33-47f2-8d15-2bf82c5ea430`
)
.expect(200);
const decryptedObject = JSON.parse(decryptResponse.text);
const secrets = JSON.parse(decryptedObject.attributes.secrets);
expect(secrets).to.eql({
params: 'some-params',
'source.inline.script': `step('Go to localhost', async () => {\n await page.goto('localhost');\n});`,
'source.project.content': '',
synthetics_args: ['param1', 'param2', 'param3'],
'ssl.key': 'some-ssl-key',
'ssl.key_passphrase': 'some-passphrase',
});
});
it(`successfully decrypts 'synthetics-param' objects`, async () => {
const decryptResponse = await supertest
.get(
`/api/hidden_saved_objects/get-decrypted-as-internal-user/synthetics-param/a304e397-723a-495c-a934-126504d53d10`
)
.expect(200);
const decryptedObject = JSON.parse(decryptResponse.text);
expect(decryptedObject.attributes.value).to.eql(
'This value was encrypted prior to the AAD include list change.'
);
});
});
});
}

View file

@ -11,5 +11,6 @@ export default function ({ loadTestFile }: FtrProviderContext) {
describe('encryptedSavedObjects', function encryptedSavedObjectsSuite() {
loadTestFile(require.resolve('./encrypted_saved_objects_api'));
loadTestFile(require.resolve('./encrypted_saved_objects_decryption'));
loadTestFile(require.resolve('./encrypted_saved_objects_aad_include_list'));
});
}