diff --git a/oas_docs/bundle.json b/oas_docs/bundle.json index f89ad4a5c938d..0f6ece5378a4a 100644 --- a/oas_docs/bundle.json +++ b/oas_docs/bundle.json @@ -26835,6 +26835,14 @@ "is_preconfigured": { "type": "boolean" }, + "kibana_api_key": { + "nullable": true, + "type": "string" + }, + "kibana_url": { + "nullable": true, + "type": "string" + }, "name": { "type": "string" }, @@ -26855,6 +26863,25 @@ "secrets": { "additionalProperties": true, "properties": { + "kibana_api_key": { + "anyOf": [ + { + "additionalProperties": true, + "properties": { + "id": { + "type": "string" + } + }, + "required": [ + "id" + ], + "type": "object" + }, + { + "type": "string" + } + ] + }, "service_token": { "anyOf": [ { @@ -26968,6 +26995,9 @@ }, "type": "object" }, + "sync_integrations": { + "type": "boolean" + }, "type": { "enum": [ "remote_elasticsearch" @@ -27925,6 +27955,14 @@ "is_preconfigured": { "type": "boolean" }, + "kibana_api_key": { + "nullable": true, + "type": "string" + }, + "kibana_url": { + "nullable": true, + "type": "string" + }, "name": { "type": "string" }, @@ -27945,6 +27983,25 @@ "secrets": { "additionalProperties": false, "properties": { + "kibana_api_key": { + "anyOf": [ + { + "additionalProperties": false, + "properties": { + "id": { + "type": "string" + } + }, + "required": [ + "id" + ], + "type": "object" + }, + { + "type": "string" + } + ] + }, "service_token": { "anyOf": [ { @@ -28058,6 +28115,9 @@ }, "type": "object" }, + "sync_integrations": { + "type": "boolean" + }, "type": { "enum": [ "remote_elasticsearch" @@ -28953,6 +29013,14 @@ "is_preconfigured": { "type": "boolean" }, + "kibana_api_key": { + "nullable": true, + "type": "string" + }, + "kibana_url": { + "nullable": true, + "type": "string" + }, "name": { "type": "string" }, @@ -28973,6 +29041,25 @@ "secrets": { "additionalProperties": true, "properties": { + "kibana_api_key": { + "anyOf": [ + { + "additionalProperties": true, + "properties": { + "id": { + "type": "string" + } + }, + "required": [ + "id" + ], + "type": "object" + }, + { + "type": "string" + } + ] + }, "service_token": { "anyOf": [ { @@ -29086,6 +29173,9 @@ }, "type": "object" }, + "sync_integrations": { + "type": "boolean" + }, "type": { "enum": [ "remote_elasticsearch" @@ -30131,6 +30221,14 @@ "is_preconfigured": { "type": "boolean" }, + "kibana_api_key": { + "nullable": true, + "type": "string" + }, + "kibana_url": { + "nullable": true, + "type": "string" + }, "name": { "type": "string" }, @@ -30151,6 +30249,25 @@ "secrets": { "additionalProperties": true, "properties": { + "kibana_api_key": { + "anyOf": [ + { + "additionalProperties": true, + "properties": { + "id": { + "type": "string" + } + }, + "required": [ + "id" + ], + "type": "object" + }, + { + "type": "string" + } + ] + }, "service_token": { "anyOf": [ { @@ -30264,6 +30381,9 @@ }, "type": "object" }, + "sync_integrations": { + "type": "boolean" + }, "type": { "enum": [ "remote_elasticsearch" @@ -31206,6 +31326,14 @@ "is_preconfigured": { "type": "boolean" }, + "kibana_api_key": { + "nullable": true, + "type": "string" + }, + "kibana_url": { + "nullable": true, + "type": "string" + }, "name": { "type": "string" }, @@ -31226,6 +31354,25 @@ "secrets": { "additionalProperties": false, "properties": { + "kibana_api_key": { + "anyOf": [ + { + "additionalProperties": false, + "properties": { + "id": { + "type": "string" + } + }, + "required": [ + "id" + ], + "type": "object" + }, + { + "type": "string" + } + ] + }, "service_token": { "anyOf": [ { @@ -31339,6 +31486,9 @@ }, "type": "object" }, + "sync_integrations": { + "type": "boolean" + }, "type": { "enum": [ "remote_elasticsearch" @@ -32219,6 +32369,14 @@ "is_preconfigured": { "type": "boolean" }, + "kibana_api_key": { + "nullable": true, + "type": "string" + }, + "kibana_url": { + "nullable": true, + "type": "string" + }, "name": { "type": "string" }, @@ -32239,6 +32397,25 @@ "secrets": { "additionalProperties": true, "properties": { + "kibana_api_key": { + "anyOf": [ + { + "additionalProperties": true, + "properties": { + "id": { + "type": "string" + } + }, + "required": [ + "id" + ], + "type": "object" + }, + { + "type": "string" + } + ] + }, "service_token": { "anyOf": [ { @@ -32352,6 +32529,9 @@ }, "type": "object" }, + "sync_integrations": { + "type": "boolean" + }, "type": { "enum": [ "remote_elasticsearch" diff --git a/oas_docs/bundle.serverless.json b/oas_docs/bundle.serverless.json index b9d0b75240c02..6689dc845ca3b 100644 --- a/oas_docs/bundle.serverless.json +++ b/oas_docs/bundle.serverless.json @@ -26835,6 +26835,14 @@ "is_preconfigured": { "type": "boolean" }, + "kibana_api_key": { + "nullable": true, + "type": "string" + }, + "kibana_url": { + "nullable": true, + "type": "string" + }, "name": { "type": "string" }, @@ -26855,6 +26863,25 @@ "secrets": { "additionalProperties": true, "properties": { + "kibana_api_key": { + "anyOf": [ + { + "additionalProperties": true, + "properties": { + "id": { + "type": "string" + } + }, + "required": [ + "id" + ], + "type": "object" + }, + { + "type": "string" + } + ] + }, "service_token": { "anyOf": [ { @@ -26968,6 +26995,9 @@ }, "type": "object" }, + "sync_integrations": { + "type": "boolean" + }, "type": { "enum": [ "remote_elasticsearch" @@ -27925,6 +27955,14 @@ "is_preconfigured": { "type": "boolean" }, + "kibana_api_key": { + "nullable": true, + "type": "string" + }, + "kibana_url": { + "nullable": true, + "type": "string" + }, "name": { "type": "string" }, @@ -27945,6 +27983,25 @@ "secrets": { "additionalProperties": false, "properties": { + "kibana_api_key": { + "anyOf": [ + { + "additionalProperties": false, + "properties": { + "id": { + "type": "string" + } + }, + "required": [ + "id" + ], + "type": "object" + }, + { + "type": "string" + } + ] + }, "service_token": { "anyOf": [ { @@ -28058,6 +28115,9 @@ }, "type": "object" }, + "sync_integrations": { + "type": "boolean" + }, "type": { "enum": [ "remote_elasticsearch" @@ -28953,6 +29013,14 @@ "is_preconfigured": { "type": "boolean" }, + "kibana_api_key": { + "nullable": true, + "type": "string" + }, + "kibana_url": { + "nullable": true, + "type": "string" + }, "name": { "type": "string" }, @@ -28973,6 +29041,25 @@ "secrets": { "additionalProperties": true, "properties": { + "kibana_api_key": { + "anyOf": [ + { + "additionalProperties": true, + "properties": { + "id": { + "type": "string" + } + }, + "required": [ + "id" + ], + "type": "object" + }, + { + "type": "string" + } + ] + }, "service_token": { "anyOf": [ { @@ -29086,6 +29173,9 @@ }, "type": "object" }, + "sync_integrations": { + "type": "boolean" + }, "type": { "enum": [ "remote_elasticsearch" @@ -30131,6 +30221,14 @@ "is_preconfigured": { "type": "boolean" }, + "kibana_api_key": { + "nullable": true, + "type": "string" + }, + "kibana_url": { + "nullable": true, + "type": "string" + }, "name": { "type": "string" }, @@ -30151,6 +30249,25 @@ "secrets": { "additionalProperties": true, "properties": { + "kibana_api_key": { + "anyOf": [ + { + "additionalProperties": true, + "properties": { + "id": { + "type": "string" + } + }, + "required": [ + "id" + ], + "type": "object" + }, + { + "type": "string" + } + ] + }, "service_token": { "anyOf": [ { @@ -30264,6 +30381,9 @@ }, "type": "object" }, + "sync_integrations": { + "type": "boolean" + }, "type": { "enum": [ "remote_elasticsearch" @@ -31206,6 +31326,14 @@ "is_preconfigured": { "type": "boolean" }, + "kibana_api_key": { + "nullable": true, + "type": "string" + }, + "kibana_url": { + "nullable": true, + "type": "string" + }, "name": { "type": "string" }, @@ -31226,6 +31354,25 @@ "secrets": { "additionalProperties": false, "properties": { + "kibana_api_key": { + "anyOf": [ + { + "additionalProperties": false, + "properties": { + "id": { + "type": "string" + } + }, + "required": [ + "id" + ], + "type": "object" + }, + { + "type": "string" + } + ] + }, "service_token": { "anyOf": [ { @@ -31339,6 +31486,9 @@ }, "type": "object" }, + "sync_integrations": { + "type": "boolean" + }, "type": { "enum": [ "remote_elasticsearch" @@ -32219,6 +32369,14 @@ "is_preconfigured": { "type": "boolean" }, + "kibana_api_key": { + "nullable": true, + "type": "string" + }, + "kibana_url": { + "nullable": true, + "type": "string" + }, "name": { "type": "string" }, @@ -32239,6 +32397,25 @@ "secrets": { "additionalProperties": true, "properties": { + "kibana_api_key": { + "anyOf": [ + { + "additionalProperties": true, + "properties": { + "id": { + "type": "string" + } + }, + "required": [ + "id" + ], + "type": "object" + }, + { + "type": "string" + } + ] + }, "service_token": { "anyOf": [ { @@ -32352,6 +32529,9 @@ }, "type": "object" }, + "sync_integrations": { + "type": "boolean" + }, "type": { "enum": [ "remote_elasticsearch" diff --git a/oas_docs/output/kibana.serverless.yaml b/oas_docs/output/kibana.serverless.yaml index 7d063ea0f61af..9aed6cd44128a 100644 --- a/oas_docs/output/kibana.serverless.yaml +++ b/oas_docs/output/kibana.serverless.yaml @@ -24805,6 +24805,12 @@ paths: type: boolean is_preconfigured: type: boolean + kibana_api_key: + nullable: true + type: string + kibana_url: + nullable: true + type: string name: type: string preset: @@ -24822,6 +24828,16 @@ paths: additionalProperties: true type: object properties: + kibana_api_key: + anyOf: + - additionalProperties: true + type: object + properties: + id: + type: string + required: + - id + - type: string service_token: anyOf: - additionalProperties: true @@ -24901,6 +24917,8 @@ paths: - certificate - strict type: string + sync_integrations: + type: boolean type: enum: - remote_elasticsearch @@ -25532,6 +25550,12 @@ paths: type: boolean is_preconfigured: type: boolean + kibana_api_key: + nullable: true + type: string + kibana_url: + nullable: true + type: string name: type: string preset: @@ -25549,6 +25573,16 @@ paths: additionalProperties: false type: object properties: + kibana_api_key: + anyOf: + - additionalProperties: false + type: object + properties: + id: + type: string + required: + - id + - type: string service_token: anyOf: - additionalProperties: false @@ -25628,6 +25662,8 @@ paths: - certificate - strict type: string + sync_integrations: + type: boolean type: enum: - remote_elasticsearch @@ -26221,6 +26257,12 @@ paths: type: boolean is_preconfigured: type: boolean + kibana_api_key: + nullable: true + type: string + kibana_url: + nullable: true + type: string name: type: string preset: @@ -26238,6 +26280,16 @@ paths: additionalProperties: true type: object properties: + kibana_api_key: + anyOf: + - additionalProperties: true + type: object + properties: + id: + type: string + required: + - id + - type: string service_token: anyOf: - additionalProperties: true @@ -26317,6 +26369,8 @@ paths: - certificate - strict type: string + sync_integrations: + type: boolean type: enum: - remote_elasticsearch @@ -27006,6 +27060,12 @@ paths: type: boolean is_preconfigured: type: boolean + kibana_api_key: + nullable: true + type: string + kibana_url: + nullable: true + type: string name: type: string preset: @@ -27023,6 +27083,16 @@ paths: additionalProperties: true type: object properties: + kibana_api_key: + anyOf: + - additionalProperties: true + type: object + properties: + id: + type: string + required: + - id + - type: string service_token: anyOf: - additionalProperties: true @@ -27102,6 +27172,8 @@ paths: - certificate - strict type: string + sync_integrations: + type: boolean type: enum: - remote_elasticsearch @@ -27720,6 +27792,12 @@ paths: type: boolean is_preconfigured: type: boolean + kibana_api_key: + nullable: true + type: string + kibana_url: + nullable: true + type: string name: type: string preset: @@ -27737,6 +27815,16 @@ paths: additionalProperties: false type: object properties: + kibana_api_key: + anyOf: + - additionalProperties: false + type: object + properties: + id: + type: string + required: + - id + - type: string service_token: anyOf: - additionalProperties: false @@ -27816,6 +27904,8 @@ paths: - certificate - strict type: string + sync_integrations: + type: boolean type: enum: - remote_elasticsearch @@ -28396,6 +28486,12 @@ paths: type: boolean is_preconfigured: type: boolean + kibana_api_key: + nullable: true + type: string + kibana_url: + nullable: true + type: string name: type: string preset: @@ -28413,6 +28509,16 @@ paths: additionalProperties: true type: object properties: + kibana_api_key: + anyOf: + - additionalProperties: true + type: object + properties: + id: + type: string + required: + - id + - type: string service_token: anyOf: - additionalProperties: true @@ -28492,6 +28598,8 @@ paths: - certificate - strict type: string + sync_integrations: + type: boolean type: enum: - remote_elasticsearch diff --git a/oas_docs/output/kibana.yaml b/oas_docs/output/kibana.yaml index f8dbe07f174e9..6c9fb9501c165 100644 --- a/oas_docs/output/kibana.yaml +++ b/oas_docs/output/kibana.yaml @@ -26794,6 +26794,12 @@ paths: type: boolean is_preconfigured: type: boolean + kibana_api_key: + nullable: true + type: string + kibana_url: + nullable: true + type: string name: type: string preset: @@ -26811,6 +26817,16 @@ paths: additionalProperties: true type: object properties: + kibana_api_key: + anyOf: + - additionalProperties: true + type: object + properties: + id: + type: string + required: + - id + - type: string service_token: anyOf: - additionalProperties: true @@ -26890,6 +26906,8 @@ paths: - certificate - strict type: string + sync_integrations: + type: boolean type: enum: - remote_elasticsearch @@ -27520,6 +27538,12 @@ paths: type: boolean is_preconfigured: type: boolean + kibana_api_key: + nullable: true + type: string + kibana_url: + nullable: true + type: string name: type: string preset: @@ -27537,6 +27561,16 @@ paths: additionalProperties: false type: object properties: + kibana_api_key: + anyOf: + - additionalProperties: false + type: object + properties: + id: + type: string + required: + - id + - type: string service_token: anyOf: - additionalProperties: false @@ -27616,6 +27650,8 @@ paths: - certificate - strict type: string + sync_integrations: + type: boolean type: enum: - remote_elasticsearch @@ -28209,6 +28245,12 @@ paths: type: boolean is_preconfigured: type: boolean + kibana_api_key: + nullable: true + type: string + kibana_url: + nullable: true + type: string name: type: string preset: @@ -28226,6 +28268,16 @@ paths: additionalProperties: true type: object properties: + kibana_api_key: + anyOf: + - additionalProperties: true + type: object + properties: + id: + type: string + required: + - id + - type: string service_token: anyOf: - additionalProperties: true @@ -28305,6 +28357,8 @@ paths: - certificate - strict type: string + sync_integrations: + type: boolean type: enum: - remote_elasticsearch @@ -28992,6 +29046,12 @@ paths: type: boolean is_preconfigured: type: boolean + kibana_api_key: + nullable: true + type: string + kibana_url: + nullable: true + type: string name: type: string preset: @@ -29009,6 +29069,16 @@ paths: additionalProperties: true type: object properties: + kibana_api_key: + anyOf: + - additionalProperties: true + type: object + properties: + id: + type: string + required: + - id + - type: string service_token: anyOf: - additionalProperties: true @@ -29088,6 +29158,8 @@ paths: - certificate - strict type: string + sync_integrations: + type: boolean type: enum: - remote_elasticsearch @@ -29705,6 +29777,12 @@ paths: type: boolean is_preconfigured: type: boolean + kibana_api_key: + nullable: true + type: string + kibana_url: + nullable: true + type: string name: type: string preset: @@ -29722,6 +29800,16 @@ paths: additionalProperties: false type: object properties: + kibana_api_key: + anyOf: + - additionalProperties: false + type: object + properties: + id: + type: string + required: + - id + - type: string service_token: anyOf: - additionalProperties: false @@ -29801,6 +29889,8 @@ paths: - certificate - strict type: string + sync_integrations: + type: boolean type: enum: - remote_elasticsearch @@ -30381,6 +30471,12 @@ paths: type: boolean is_preconfigured: type: boolean + kibana_api_key: + nullable: true + type: string + kibana_url: + nullable: true + type: string name: type: string preset: @@ -30398,6 +30494,16 @@ paths: additionalProperties: true type: object properties: + kibana_api_key: + anyOf: + - additionalProperties: true + type: object + properties: + id: + type: string + required: + - id + - type: string service_token: anyOf: - additionalProperties: true @@ -30477,6 +30583,8 @@ paths: - certificate - strict type: string + sync_integrations: + type: boolean type: enum: - remote_elasticsearch diff --git a/packages/kbn-check-mappings-update-cli/current_mappings.json b/packages/kbn-check-mappings-update-cli/current_mappings.json index be5d77ad2e8c0..d8bebc391f0fb 100644 --- a/packages/kbn-check-mappings-update-cli/current_mappings.json +++ b/packages/kbn-check-mappings-update-cli/current_mappings.json @@ -2115,6 +2115,7 @@ } }, "ingest-outputs": { + "dynamic": false, "properties": { "allow_edit": { "enabled": false diff --git a/src/core/server/integration_tests/ci_checks/saved_objects/check_registered_types.test.ts b/src/core/server/integration_tests/ci_checks/saved_objects/check_registered_types.test.ts index c202529d692c2..d519769b09038 100644 --- a/src/core/server/integration_tests/ci_checks/saved_objects/check_registered_types.test.ts +++ b/src/core/server/integration_tests/ci_checks/saved_objects/check_registered_types.test.ts @@ -126,7 +126,7 @@ describe('checking migration metadata changes on all registered SO types', () => "infrastructure-ui-source": "113182d6895764378dfe7fa9fa027244f3a457c4", "ingest-agent-policies": "57ebfb047cf0b81c6fa0ceed8586fa7199c7c5e2", "ingest-download-sources": "279a68147e62e4d8858c09ad1cf03bd5551ce58d", - "ingest-outputs": "55988d5f778bbe0e76caa7e6468707a0a056bdd8", + "ingest-outputs": "6743521f501bd77b1523dbb1df48d7c47fdad529", "ingest-package-policies": "870f8c21fe3602f31075430a1fdfb052c62d4a14", "ingest_manager_settings": "111a616eb72627c002029c19feb9e6c439a10505", "inventory-view": "fd2b7fe713956f261018dded00d8f8c986417763", diff --git a/x-pack/platform/plugins/shared/fleet/common/experimental_features.ts b/x-pack/platform/plugins/shared/fleet/common/experimental_features.ts index b0a1fa29e36c9..7866e847897b9 100644 --- a/x-pack/platform/plugins/shared/fleet/common/experimental_features.ts +++ b/x-pack/platform/plugins/shared/fleet/common/experimental_features.ts @@ -11,6 +11,7 @@ const _allowedExperimentalValues = { showExperimentalShipperOptions: false, useSpaceAwareness: false, enableAutomaticAgentUpgrades: false, + enableSyncIntegrationsOnRemote: false, }; /** diff --git a/x-pack/platform/plugins/shared/fleet/common/types/models/output.ts b/x-pack/platform/plugins/shared/fleet/common/types/models/output.ts index b47a393013eda..76bd16d062197 100644 --- a/x-pack/platform/plugins/shared/fleet/common/types/models/output.ts +++ b/x-pack/platform/plugins/shared/fleet/common/types/models/output.ts @@ -65,7 +65,11 @@ export interface NewRemoteElasticsearchOutput extends NewBaseOutput { service_token?: string | null; secrets?: { service_token?: OutputSecret; + kibana_api_key?: OutputSecret; }; + sync_integrations?: boolean; + kibana_url?: string | null; + kibana_api_key?: string | null; } export interface NewLogstashOutput extends NewBaseOutput { diff --git a/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/index.test.tsx b/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/index.test.tsx index 05d66fff15c8c..69545da581b8c 100644 --- a/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/index.test.tsx +++ b/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/index.test.tsx @@ -319,7 +319,9 @@ describe('EditOutputFlyout', () => { }); it('should render the flyout if the output provided is a remote ES output', async () => { - jest.spyOn(ExperimentalFeaturesService, 'get').mockReturnValue({} as any); + jest + .spyOn(ExperimentalFeaturesService, 'get') + .mockReturnValue({ enableSyncIntegrationsOnRemote: true } as any); mockedUseFleetStatus.mockReturnValue({ isLoading: false, @@ -333,6 +335,8 @@ describe('EditOutputFlyout', () => { id: 'outputR', is_default: false, is_default_monitoring: false, + kibana_url: 'http://localhost', + sync_integrations: true, }); remoteEsOutputLabels.forEach((label) => { @@ -345,10 +349,18 @@ describe('EditOutputFlyout', () => { ); expect(utils.queryByTestId('serviceTokenSecretInput')).not.toBeNull(); + + expect(utils.queryByTestId('kibanaAPIKeyCallout')).not.toBeNull(); + expect( + (utils.getByTestId('settingsOutputsFlyout.kibanaURLInput') as HTMLInputElement).value + ).toEqual('http://localhost'); + expect(utils.queryByTestId('kibanaAPIKeySecretInput')).not.toBeNull(); }); it('should populate secret service token input with plain text value when editing remote ES output', async () => { - jest.spyOn(ExperimentalFeaturesService, 'get').mockReturnValue({} as any); + jest + .spyOn(ExperimentalFeaturesService, 'get') + .mockReturnValue({ enableSyncIntegrationsOnRemote: true } as any); mockedUseFleetStatus.mockReturnValue({ isLoading: false, @@ -364,11 +376,13 @@ describe('EditOutputFlyout', () => { is_default_monitoring: false, service_token: '1234', hosts: ['https://localhost:9200'], + kibana_api_key: 'key', }); expect((utils.getByTestId('serviceTokenSecretInput') as HTMLInputElement).value).toEqual( '1234' ); + expect((utils.getByTestId('kibanaAPIKeySecretInput') as HTMLInputElement).value).toEqual('key'); fireEvent.click(utils.getByText('Save and apply settings')); @@ -376,8 +390,9 @@ describe('EditOutputFlyout', () => { expect(mockSendPutOutput).toHaveBeenCalledWith( 'outputR', expect.objectContaining({ - secrets: { service_token: '1234' }, + secrets: { service_token: '1234', kibana_api_key: 'key' }, service_token: undefined, + kibana_api_key: undefined, }) ); }); diff --git a/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/output_form_remote_es.tsx b/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/output_form_remote_es.tsx index 658a42115381b..9aa5d5f830b64 100644 --- a/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/output_form_remote_es.tsx +++ b/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/output_form_remote_es.tsx @@ -6,12 +6,21 @@ */ import React, { useEffect } from 'react'; -import { EuiCallOut, EuiCodeBlock, EuiFieldText, EuiSpacer } from '@elastic/eui'; +import { + EuiCallOut, + EuiCodeBlock, + EuiFieldText, + EuiFormRow, + EuiSpacer, + EuiSwitch, +} from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import { i18n } from '@kbn/i18n'; import { MultiRowInput } from '../multi_row_input'; +import { ExperimentalFeaturesService } from '../../../../services'; + import type { OutputFormInputsType } from './use_output_form'; import { SecretFormRow } from './output_form_secret_form_row'; @@ -25,7 +34,9 @@ export const OutputFormRemoteEsSection: React.FunctionComponent = (props) const { inputs, useSecretsStorage, onToggleSecretStorage } = props; const [isConvertedToSecret, setIsConvertedToSecret] = React.useState({ serviceToken: false, + kibanaAPIKey: false, }); + const { enableSyncIntegrationsOnRemote } = ExperimentalFeaturesService.get(); const [isFirstLoad, setIsFirstLoad] = React.useState(true); @@ -34,16 +45,30 @@ export const OutputFormRemoteEsSection: React.FunctionComponent = (props) setIsFirstLoad(false); // populate the secret input with the value of the plain input in order to re-save the output with secret storage if (useSecretsStorage) { + let isServiceTokenSecret = false; if (inputs.serviceTokenInput.value && !inputs.serviceTokenSecretInput.value) { inputs.serviceTokenSecretInput.setValue(inputs.serviceTokenInput.value); inputs.serviceTokenInput.clear(); - setIsConvertedToSecret({ serviceToken: true }); + isServiceTokenSecret = true; + } + let isKibanaAPIKeySecret = false; + if (inputs.kibanaAPIKeyInput.value && !inputs.kibanaAPIKeySecretInput.value) { + inputs.kibanaAPIKeySecretInput.setValue(inputs.kibanaAPIKeyInput.value); + inputs.kibanaAPIKeyInput.clear(); + isKibanaAPIKeySecret = true; } + setIsConvertedToSecret({ + ...isConvertedToSecret, + serviceToken: isServiceTokenSecret, + kibanaAPIKey: isKibanaAPIKeySecret, + }); } }, [ useSecretsStorage, inputs.serviceTokenInput, inputs.serviceTokenSecretInput, + inputs.kibanaAPIKeyInput, + inputs.kibanaAPIKeySecretInput, isFirstLoad, setIsFirstLoad, isConvertedToSecret, @@ -52,10 +77,12 @@ export const OutputFormRemoteEsSection: React.FunctionComponent = (props) const onToggleSecretAndClearValue = (secretEnabled: boolean) => { if (secretEnabled) { inputs.serviceTokenInput.clear(); + inputs.kibanaAPIKeyInput.clear(); } else { inputs.serviceTokenSecretInput.setValue(''); + inputs.kibanaAPIKeySecretInput.setValue(''); } - setIsConvertedToSecret({ ...isConvertedToSecret, serviceToken: false }); + setIsConvertedToSecret({ ...isConvertedToSecret, serviceToken: false, kibanaAPIKey: false }); onToggleSecretStorage(secretEnabled); }; @@ -144,6 +171,132 @@ export const OutputFormRemoteEsSection: React.FunctionComponent = (props) + {enableSyncIntegrationsOnRemote ? ( + <> + + } + {...inputs.syncIntegrationsInput.formRowProps} + > + + } + /> + + + } + {...inputs.kibanaURLInput.formRowProps} + > + + + + {!useSecretsStorage ? ( + + } + {...inputs.kibanaAPIKeyInput.formRowProps} + useSecretsStorage={useSecretsStorage} + onToggleSecretStorage={onToggleSecretAndClearValue} + > + + + ) : ( + + + + )} + + + } + data-test-subj="kibanaAPIKeyCallout" + > + + {` POST /_security/api_key + { + "name": "integration_sync_api_key", + "role_descriptors": { + "integration_writer": { + "cluster": [], + "indices":[], + "applications": [{ + "application": "kibana-.kibana", + "privileges": ["feature_fleet.read", "feature_fleetv2.read"], + "resources": ["*"] + }] + } + } + }`} + + + + + ) : null} ); }; diff --git a/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/output_form_validators.test.tsx b/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/output_form_validators.test.tsx index 2e17153ee34e7..888b815172bc2 100644 --- a/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/output_form_validators.test.tsx +++ b/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/output_form_validators.test.tsx @@ -12,6 +12,8 @@ import { validateCATrustedFingerPrint, validateKafkaHeaders, validateKafkaHosts, + validateKibanaURL, + validateKibanaAPIKey, } from './output_form_validators'; describe('Output form validation', () => { @@ -130,6 +132,58 @@ describe('Output form validation', () => { }); }); + describe('validateKibanaURL', () => { + it('should not work with empty url', () => { + const res = validateKibanaURL('', true); + + expect(res).toEqual(['URL is required']); + }); + + it('should work with empty url if syncEnabled is false', () => { + const res = validateKibanaURL('', false); + + expect(res).toBeUndefined(); + }); + + it('should work with valid url', () => { + const res = validateKibanaURL('https://test.fr:9200', true); + + expect(res).toBeUndefined(); + }); + + it('should return an error with invalid url', () => { + const res = validateKibanaURL('toto', false); + + expect(res).toEqual(['Invalid URL']); + }); + + it('should return an error with url with invalid port', () => { + const res = validateKibanaURL('https://test.fr:qwerty9200', true); + + expect(res).toEqual(['Invalid URL']); + }); + + it('should return an error when invalid protocol', () => { + const res = validateKibanaURL('ftp://test.fr', false); + + expect(res).toEqual(['Invalid protocol']); + }); + }); + + describe('validateKibanaAPIKey', () => { + it('should not work with empty url', () => { + const res = validateKibanaAPIKey(''); + + expect(res).toEqual(['Kibana API Key is required']); + }); + + it('should work with valid url', () => { + const res = validateKibanaAPIKey('apikey'); + + expect(res).toBeUndefined(); + }); + }); + describe('validateLogstashHosts', () => { it('should not work without any urls', () => { const res = validateLogstashHosts([]); diff --git a/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/output_form_validators.tsx b/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/output_form_validators.tsx index 5e3bf5b3725e9..14d2430f5121a 100644 --- a/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/output_form_validators.tsx +++ b/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/output_form_validators.tsx @@ -241,6 +241,35 @@ export function validateName(value: string) { } } +export function validateKibanaURL(val: string, syncEnabled: boolean) { + try { + if (syncEnabled && !val) { + return [ + i18n.translate('xpack.fleet.settings.outputForm.urlRequiredError', { + defaultMessage: 'URL is required', + }), + ]; + } else if (!val) { + return; + } else { + const urlParsed = new URL(val); + if (!['http:', 'https:'].includes(urlParsed.protocol)) { + return [ + i18n.translate('xpack.fleet.settings.outputForm.invalidProtocolError', { + defaultMessage: 'Invalid protocol', + }), + ]; + } + } + } catch (error) { + return [ + i18n.translate('xpack.fleet.settings.outputForm.invalidURLError', { + defaultMessage: 'Invalid URL', + }), + ]; + } +} + export function validateKafkaUsername(value: string) { if (!value || value === '') { return [ @@ -286,6 +315,18 @@ export function validateServiceToken(value: string) { export const validateServiceTokenSecret = toSecretValidator(validateServiceToken); +export function validateKibanaAPIKey(value: string) { + if (!value || value === '') { + return [ + i18n.translate('xpack.fleet.settings.outputForm.kibanaAPIKeyRequiredErrorMessage', { + defaultMessage: 'Kibana API Key is required', + }), + ]; + } +} + +export const validateKibanaAPIKeySecret = toSecretValidator(validateKibanaAPIKey); + export function validateSSLCertificate(value: string) { if (!value || value === '') { return [ diff --git a/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/use_output_form.tsx b/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/use_output_form.tsx index e47e271872309..d2dfb92057289 100644 --- a/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/use_output_form.tsx +++ b/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/use_output_form.tsx @@ -74,6 +74,9 @@ import { validateKafkaHosts, validateKafkaPartitioningGroupEvents, validateDynamicKafkaTopics, + validateKibanaURL, + validateKibanaAPIKey, + validateKibanaAPIKeySecret, } from './output_form_validators'; import { confirmUpdate } from './confirm_update'; @@ -97,6 +100,10 @@ export interface OutputFormInputsType { caTrustedFingerprintInput: ReturnType; serviceTokenInput: ReturnType; serviceTokenSecretInput: ReturnType; + syncIntegrationsInput: ReturnType; + kibanaURLInput: ReturnType; + kibanaAPIKeyInput: ReturnType; + kibanaAPIKeySecretInput: ReturnType; sslCertificateInput: ReturnType; sslKeyInput: ReturnType; sslKeySecretInput: ReturnType; @@ -262,7 +269,7 @@ export function useOutputForm(onSucess: () => void, output?: Output, defaultOupu isDisabled('preset') ); - // Remtote ES inputs + // Remote ES inputs const serviceTokenInput = useInput( (output as NewRemoteElasticsearchOutput)?.service_token ?? '', validateServiceToken, @@ -274,6 +281,29 @@ export function useOutputForm(onSucess: () => void, output?: Output, defaultOupu validateServiceTokenSecret, isDisabled('service_token') ); + + const syncIntegrationsInput = useSwitchInput( + (output as NewRemoteElasticsearchOutput)?.sync_integrations ?? false, + isDisabled('sync_integrations') + ); + + const kibanaAPIKeyInput = useInput( + (output as NewRemoteElasticsearchOutput)?.kibana_api_key ?? '', + syncIntegrationsInput.value ? validateKibanaAPIKey : undefined, + isDisabled('kibana_api_key') + ); + + const kibanaAPIKeySecretInput = useSecretInput( + (output as NewRemoteElasticsearchOutput)?.secrets?.kibana_api_key ?? '', + syncIntegrationsInput.value ? validateKibanaAPIKeySecret : undefined, + isDisabled('kibana_api_key') + ); + + const kibanaURLInput = useInput( + (output as NewRemoteElasticsearchOutput)?.kibana_url ?? '', + (val) => validateKibanaURL(val, syncIntegrationsInput.value), + isDisabled('kibana_url') + ); /* Shipper feature flag - currently depends on the content of the yaml # Enables the shipper: @@ -556,6 +586,10 @@ export function useOutputForm(onSucess: () => void, output?: Output, defaultOupu caTrustedFingerprintInput, serviceTokenInput, serviceTokenSecretInput, + kibanaAPIKeyInput, + kibanaAPIKeySecretInput, + syncIntegrationsInput, + kibanaURLInput, sslCertificateInput, sslKeyInput, sslKeySecretInput, @@ -615,6 +649,9 @@ export function useOutputForm(onSucess: () => void, output?: Output, defaultOupu const caTrustedFingerprintValid = caTrustedFingerprintInput.validate(); const serviceTokenValid = serviceTokenInput.validate(); const serviceTokenSecretValid = serviceTokenSecretInput.validate(); + const kibanaAPIKeyValid = kibanaAPIKeyInput.validate(); + const kibanaAPIKeySecretValid = kibanaAPIKeySecretInput.validate(); + const kibanaURLInputValid = kibanaURLInput.validate(); const sslCertificateValid = sslCertificateInput.validate(); const sslKeyValid = sslKeyInput.validate(); const sslKeySecretValid = sslKeySecretInput.validate(); @@ -666,7 +703,12 @@ export function useOutputForm(onSucess: () => void, output?: Output, defaultOupu additionalYamlConfigValid && nameInputValid && ((serviceTokenInput.value && serviceTokenValid) || - (serviceTokenSecretInput.value && serviceTokenSecretValid)) + (serviceTokenSecretInput.value && serviceTokenSecretValid)) && + ((!syncIntegrationsInput.value && kibanaURLInputValid) || + (syncIntegrationsInput.value && + ((kibanaAPIKeyInput.value && kibanaAPIKeyValid) || + (kibanaAPIKeySecretInput.value && kibanaAPIKeySecretValid)) && + kibanaURLInputValid)) ); } else { // validate ES @@ -695,6 +737,10 @@ export function useOutputForm(onSucess: () => void, output?: Output, defaultOupu caTrustedFingerprintInput, serviceTokenInput, serviceTokenSecretInput, + kibanaAPIKeyInput, + kibanaAPIKeySecretInput, + syncIntegrationsInput, + kibanaURLInput, sslCertificateInput, sslKeyInput, sslKeySecretInput, @@ -912,6 +958,18 @@ export function useOutputForm(onSucess: () => void, output?: Output, defaultOupu ...shipperParams, } as NewLogstashOutput; case outputType.RemoteElasticsearch: + let secrets; + if (!serviceTokenInput.value && serviceTokenSecretInput.value) { + secrets = { + service_token: serviceTokenSecretInput.value, + }; + } + if (!kibanaAPIKeyInput.value && kibanaAPIKeySecretInput.value) { + secrets = { + ...(secrets ?? {}), + kibana_api_key: kibanaAPIKeySecretInput.value, + }; + } return { name: nameInput.value, type: outputType.RemoteElasticsearch, @@ -921,12 +979,10 @@ export function useOutputForm(onSucess: () => void, output?: Output, defaultOupu preset: presetInput.value, config_yaml: additionalYamlConfigInput.value, service_token: serviceTokenInput.value || undefined, - ...(!serviceTokenInput.value && - serviceTokenSecretInput.value && { - secrets: { - service_token: serviceTokenSecretInput.value, - }, - }), + kibana_api_key: kibanaAPIKeyInput.value || undefined, + ...(secrets ? { secrets } : {}), + sync_integrations: syncIntegrationsInput.value, + kibana_url: kibanaURLInput.value || null, proxy_id: proxyIdValue, ...shipperParams, } as NewRemoteElasticsearchOutput; @@ -1036,6 +1092,10 @@ export function useOutputForm(onSucess: () => void, output?: Output, defaultOupu presetInput.value, serviceTokenInput.value, serviceTokenSecretInput.value, + kibanaAPIKeyInput.value, + kibanaAPIKeySecretInput.value, + syncIntegrationsInput.value, + kibanaURLInput.value, caTrustedFingerprintInput.value, confirm, notifications.toasts, diff --git a/x-pack/platform/plugins/shared/fleet/server/routes/output/handler.test.ts b/x-pack/platform/plugins/shared/fleet/server/routes/output/handler.test.ts index 665cb9059654e..e2c63dd4c796a 100644 --- a/x-pack/platform/plugins/shared/fleet/server/routes/output/handler.test.ts +++ b/x-pack/platform/plugins/shared/fleet/server/routes/output/handler.test.ts @@ -258,4 +258,41 @@ describe('output handler', () => { expect(res).toEqual({ body: { item: { id: 'output1' } } }); }); + + it('should return error if both kibana_api_key and secrets.kibana_api_key is provided for remote_elasticsearch output', async () => { + jest + .spyOn(appContextService, 'getCloud') + .mockReturnValue({ isServerlessEnabled: false } as any); + + const res = await postOutputHandlerWithErrorHandler( + mockContext, + { + body: { + type: 'remote_elasticsearch', + kibana_api_key: 'value1', + secrets: { kibana_api_key: 'value2' }, + }, + } as any, + mockResponse as any + ); + + expect(res).toEqual({ + body: { message: 'Cannot specify both kibana_api_key and secrets.kibana_api_key' }, + statusCode: 400, + }); + }); + + it('should return ok if one of kibana_api_key and secrets.kibana_api_key is provided for remote_elasticsearch output', async () => { + jest + .spyOn(appContextService, 'getCloud') + .mockReturnValue({ isServerlessEnabled: false } as any); + + const res = await postOutputHandlerWithErrorHandler( + mockContext, + { body: { type: 'remote_elasticsearch', secrets: { kibana_api_key: 'value2' } } } as any, + mockResponse as any + ); + + expect(res).toEqual({ body: { item: { id: 'output1' } } }); + }); }); diff --git a/x-pack/platform/plugins/shared/fleet/server/routes/output/handler.ts b/x-pack/platform/plugins/shared/fleet/server/routes/output/handler.ts index 0b33dd15e73fe..f760870424a9e 100644 --- a/x-pack/platform/plugins/shared/fleet/server/routes/output/handler.ts +++ b/x-pack/platform/plugins/shared/fleet/server/routes/output/handler.ts @@ -44,12 +44,13 @@ function ensureNoDuplicateSecrets(output: Partial) { ) { throw Boom.badRequest('Cannot specify both ssl.key and secrets.ssl.key'); } - if ( - output.type === outputType.RemoteElasticsearch && - output.service_token && - output.secrets?.service_token - ) { - throw Boom.badRequest('Cannot specify both service_token and secrets.service_token'); + if (output.type === outputType.RemoteElasticsearch) { + if (output.service_token && output.secrets?.service_token) { + throw Boom.badRequest('Cannot specify both service_token and secrets.service_token'); + } + if (output.kibana_api_key && output.secrets?.kibana_api_key) { + throw Boom.badRequest('Cannot specify both kibana_api_key and secrets.kibana_api_key'); + } } } diff --git a/x-pack/platform/plugins/shared/fleet/server/saved_objects/index.ts b/x-pack/platform/plugins/shared/fleet/server/saved_objects/index.ts index 94292f9a28bd5..fbbbd5d34137a 100644 --- a/x-pack/platform/plugins/shared/fleet/server/saved_objects/index.ts +++ b/x-pack/platform/plugins/shared/fleet/server/saved_objects/index.ts @@ -402,6 +402,7 @@ export const getSavedObjectTypes = ( importableAndExportable: false, }, mappings: { + dynamic: false, properties: { output_id: { type: 'keyword', index: false }, name: { type: 'keyword' }, @@ -613,6 +614,14 @@ export const getSavedObjectTypes = ( }, ], }, + '8': { + changes: [ + { + type: 'mappings_addition', + addedMappings: {}, + }, + ], + }, }, migrations: { '7.13.0': migrateOutputToV7130, diff --git a/x-pack/platform/plugins/shared/fleet/server/services/agent_policies/__snapshots__/full_agent_policy.test.ts.snap b/x-pack/platform/plugins/shared/fleet/server/services/agent_policies/__snapshots__/full_agent_policy.test.ts.snap index 9d93fd5df3c81..5b17b992a56c8 100644 --- a/x-pack/platform/plugins/shared/fleet/server/services/agent_policies/__snapshots__/full_agent_policy.test.ts.snap +++ b/x-pack/platform/plugins/shared/fleet/server/services/agent_policies/__snapshots__/full_agent_policy.test.ts.snap @@ -309,8 +309,11 @@ Object { "hosts": Array [ "http://127.0.0.1:9201", ], + "kibana_api_key": undefined, + "kibana_url": undefined, "preset": "balanced", "service_token": undefined, + "sync_integrations": undefined, "type": "remote_elasticsearch", }, }, diff --git a/x-pack/platform/plugins/shared/fleet/server/services/agent_policies/full_agent_policy.ts b/x-pack/platform/plugins/shared/fleet/server/services/agent_policies/full_agent_policy.ts index ea14167bb324a..c49752fd42f78 100644 --- a/x-pack/platform/plugins/shared/fleet/server/services/agent_policies/full_agent_policy.ts +++ b/x-pack/platform/plugins/shared/fleet/server/services/agent_policies/full_agent_policy.ts @@ -518,6 +518,9 @@ export function transformOutputToFullPolicyOutput( if (output.type === outputType.RemoteElasticsearch) { newOutput.service_token = output.service_token; + newOutput.kibana_api_key = output.kibana_api_key; + newOutput.kibana_url = output.kibana_url; + newOutput.sync_integrations = output.sync_integrations; } if (outputTypeSupportPresets(output.type)) { diff --git a/x-pack/platform/plugins/shared/fleet/server/services/output.test.ts b/x-pack/platform/plugins/shared/fleet/server/services/output.test.ts index addb48449e520..9d609e7e7d68d 100644 --- a/x-pack/platform/plugins/shared/fleet/server/services/output.test.ts +++ b/x-pack/platform/plugins/shared/fleet/server/services/output.test.ts @@ -2183,6 +2183,7 @@ describe('Output Service', () => { expect(soClient.update).toBeCalledWith(expect.anything(), expect.anything(), { type: 'remote_elasticsearch', + kibana_api_key: null, service_token: null, }); }); diff --git a/x-pack/platform/plugins/shared/fleet/server/services/output.ts b/x-pack/platform/plugins/shared/fleet/server/services/output.ts index a33ef9fc9e9fc..06eb3102d8eec 100644 --- a/x-pack/platform/plugins/shared/fleet/server/services/output.ts +++ b/x-pack/platform/plugins/shared/fleet/server/services/output.ts @@ -696,6 +696,9 @@ class OutputService { if (!output.service_token && output.secrets?.service_token) { data.service_token = output.secrets?.service_token as string; } + if (!output.kibana_api_key && output.secrets?.kibana_api_key) { + data.kibana_api_key = output.secrets?.kibana_api_key as string; + } } } @@ -950,6 +953,7 @@ class OutputService { updateData.ca_trusted_fingerprint = null; updateData.ca_sha256 = null; delete (updateData as Nullable).service_token; + delete (updateData as Nullable).kibana_api_key; } if (data.type !== outputType.Logstash) { @@ -1072,6 +1076,9 @@ class OutputService { if (!data.service_token) { updateData.service_token = null; } + if (!data.kibana_api_key) { + updateData.kibana_api_key = null; + } } if (!data.preset && data.type === outputType.Elasticsearch) { @@ -1121,6 +1128,9 @@ class OutputService { if (!data.service_token && data.secrets?.service_token) { updateData.service_token = data.secrets?.service_token as string; } + if (!data.kibana_api_key && data.secrets?.kibana_api_key) { + updateData.kibana_api_key = data.secrets?.kibana_api_key as string; + } } } diff --git a/x-pack/platform/plugins/shared/fleet/server/services/preconfiguration/outputs.test.ts b/x-pack/platform/plugins/shared/fleet/server/services/preconfiguration/outputs.test.ts index 35f1d29470919..e4aa34c0bc5c8 100644 --- a/x-pack/platform/plugins/shared/fleet/server/services/preconfiguration/outputs.test.ts +++ b/x-pack/platform/plugins/shared/fleet/server/services/preconfiguration/outputs.test.ts @@ -79,6 +79,7 @@ describe('output preconfiguration', () => { const keyHash = (await hashSecret('secretKey')) as string; const passwordHash = (await hashSecret('secretPassword')) as string; const serviceTokenHash = (await hashSecret('secretServiceToken')) as string; + const serviceKibanaAPIKeyHash = (await hashSecret('secretKibanaAPIKey')) as string; mockedOutputService.bulkGet.mockImplementation(async (soClient, id): Promise => { return [ { @@ -221,6 +222,7 @@ describe('output preconfiguration', () => { hosts: ['https:es.co:80'], is_preconfigured: true, service_token: 'unsecureServiceToken', + kibana_api_key: 'unsecureKibanaApiKey', }, { id: 'existing-remote-es-output-with-secrets-1', @@ -235,6 +237,10 @@ describe('output preconfiguration', () => { id: '101112', hash: serviceTokenHash, }, + kibana_api_key: { + id: '131415', + hash: serviceKibanaAPIKeyHash, + }, }, }, { @@ -247,6 +253,7 @@ describe('output preconfiguration', () => { is_preconfigured: true, secrets: { service_token: 'secretServiceToken', + kibana_api_key: 'secretKibanaAPIKey', }, }, ]; @@ -456,6 +463,9 @@ describe('output preconfiguration', () => { is_default_monitoring: false, hosts: ['test.fr'], service_token: 'serviceToken', + kibana_api_key: 'kibanaAPIKey', + kibana_url: 'http://kibana.co', + sync_integrations: true, } as PreconfiguredOutput, ]); @@ -809,6 +819,7 @@ describe('output preconfiguration', () => { is_preconfigured: true, secrets: { service_token: 'secretServiceToken', // no change + kibana_api_key: 'secretKibanaAPIKey', }, }, ]); @@ -898,6 +909,7 @@ describe('output preconfiguration', () => { hosts: ['https:es.co:80'], is_preconfigured: true, service_token: 'unsecureServiceToken', + kibana_api_key: 'unsecureKibanaAPIKey', } as PreconfiguredOutput, ]); @@ -969,6 +981,7 @@ describe('output preconfiguration', () => { is_preconfigured: true, secrets: { service_token: 'secretServiceToken', + kibana_api_key: 'secretKibanaAPIKey', }, }, ]); diff --git a/x-pack/platform/plugins/shared/fleet/server/services/preconfiguration/outputs.ts b/x-pack/platform/plugins/shared/fleet/server/services/preconfiguration/outputs.ts index f605df4680b31..6070481bc16b7 100644 --- a/x-pack/platform/plugins/shared/fleet/server/services/preconfiguration/outputs.ts +++ b/x-pack/platform/plugins/shared/fleet/server/services/preconfiguration/outputs.ts @@ -198,12 +198,21 @@ async function hashSecrets(output: PreconfiguredOutput) { } if (output.type === 'remote_elasticsearch') { const remoteESOutput = output as NewRemoteElasticsearchOutput; + let secrets; if (typeof remoteESOutput.secrets?.service_token === 'string') { const serviceToken = await hashSecret(remoteESOutput.secrets?.service_token); - return { + secrets = { service_token: serviceToken, }; } + if (typeof remoteESOutput.secrets?.kibana_api_key === 'string') { + const kibanaAPIKey = await hashSecret(remoteESOutput.secrets?.kibana_api_key); + secrets = { + ...(secrets ? secrets : {}), + kibana_api_key: kibanaAPIKey, + }; + } + return secrets; } return undefined; @@ -350,10 +359,17 @@ async function isPreconfiguredOutputDifferentFromCurrent( ) { return false; } - const serviceTokenIsDifferent = await isSecretDifferent( - preconfiguredOutput.secrets?.service_token, - existingOutput.secrets?.service_token - ); + const serviceTokenIsDifferent = + (await isSecretDifferent( + preconfiguredOutput.secrets?.service_token, + existingOutput.secrets?.service_token + )) || + (await isSecretDifferent( + preconfiguredOutput.secrets?.kibana_api_key, + existingOutput.secrets?.kibana_api_key + )) || + isDifferent(existingOutput.kibana_url, preconfiguredOutput.kibana_url) || + isDifferent(existingOutput.sync_integrations, preconfiguredOutput.sync_integrations); return serviceTokenIsDifferent; }; diff --git a/x-pack/platform/plugins/shared/fleet/server/services/secrets.ts b/x-pack/platform/plugins/shared/fleet/server/services/secrets.ts index 764340e1b5eb3..16f3b9d21c7eb 100644 --- a/x-pack/platform/plugins/shared/fleet/server/services/secrets.ts +++ b/x-pack/platform/plugins/shared/fleet/server/services/secrets.ts @@ -333,6 +333,12 @@ function getOutputSecretPaths( value: remoteESOutput.secrets.service_token, }); } + if (remoteESOutput.secrets?.kibana_api_key) { + outputSecretPaths.push({ + path: 'secrets.kibana_api_key', + value: remoteESOutput.secrets.kibana_api_key, + }); + } } return outputSecretPaths; @@ -378,13 +384,17 @@ export function getOutputSecretReferences(output: Output): PolicySecretReference }); } - if ( - output.type === 'remote_elasticsearch' && - typeof output?.secrets?.service_token === 'object' - ) { - outputSecretPaths.push({ - id: output.secrets.service_token.id, - }); + if (output.type === 'remote_elasticsearch') { + if (typeof output?.secrets?.service_token === 'object') { + outputSecretPaths.push({ + id: output.secrets.service_token.id, + }); + } + if (typeof output?.secrets?.kibana_api_key === 'object') { + outputSecretPaths.push({ + id: output.secrets.kibana_api_key.id, + }); + } } return outputSecretPaths; diff --git a/x-pack/platform/plugins/shared/fleet/server/types/models/output.ts b/x-pack/platform/plugins/shared/fleet/server/types/models/output.ts index 2f02819554d53..a8c85ea2c3ffe 100644 --- a/x-pack/platform/plugins/shared/fleet/server/types/models/output.ts +++ b/x-pack/platform/plugins/shared/fleet/server/types/models/output.ts @@ -152,8 +152,12 @@ export const RemoteElasticSearchSchema = { secrets: schema.maybe( schema.object({ service_token: schema.maybe(secretRefSchema), + kibana_api_key: schema.maybe(secretRefSchema), }) ), + sync_integrations: schema.maybe(schema.boolean()), + kibana_url: schema.maybe(schema.oneOf([schema.literal(null), schema.string()])), + kibana_api_key: schema.maybe(schema.oneOf([schema.literal(null), schema.string()])), }; const RemoteElasticSearchUpdateSchema = { @@ -163,8 +167,12 @@ const RemoteElasticSearchUpdateSchema = { secrets: schema.maybe( schema.object({ service_token: schema.maybe(secretRefSchema), + kibana_api_key: schema.maybe(secretRefSchema), }) ), + sync_integrations: schema.maybe(schema.boolean()), + kibana_url: schema.maybe(schema.oneOf([schema.literal(null), schema.string()])), + kibana_api_key: schema.maybe(schema.oneOf([schema.literal(null), schema.string()])), }; /** diff --git a/x-pack/platform/plugins/shared/fleet/server/types/so_attributes.ts b/x-pack/platform/plugins/shared/fleet/server/types/so_attributes.ts index 7998e5da9b5e5..f04dc1b3c069e 100644 --- a/x-pack/platform/plugins/shared/fleet/server/types/so_attributes.ts +++ b/x-pack/platform/plugins/shared/fleet/server/types/so_attributes.ts @@ -170,7 +170,11 @@ export interface OutputSoRemoteElasticsearchAttributes extends OutputSoBaseAttri service_token?: string; secrets?: { service_token?: { id: string }; + kibana_api_key?: { id: string }; }; + sync_integrations?: boolean; + kibana_url?: string; + kibana_api_key?: string; } interface OutputSoLogstashAttributes extends OutputSoBaseAttributes {