Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions x-pack/plugins/alerting/common/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export interface ActionGroup {

export interface AlertingFrameworkHealth {
isSufficientlySecure: boolean;
hasPermanentEncryptionKey: boolean;
}

export const BASE_ALERT_API_PATH = '/api/alert';
2 changes: 1 addition & 1 deletion x-pack/plugins/alerting/server/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ export class AlertingPlugin {
unmuteAllAlertRoute(router, this.licenseState);
muteAlertInstanceRoute(router, this.licenseState);
unmuteAlertInstanceRoute(router, this.licenseState);
healthRoute(router, this.licenseState);
healthRoute(router, this.licenseState, plugins.encryptedSavedObjects);

return {
registerType: alertTypeRegistry.register.bind(alertTypeRegistry),
Expand Down
58 changes: 51 additions & 7 deletions x-pack/plugins/alerting/server/routes/health.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { mockHandlerArguments } from './_mock_handler_arguments';
import { elasticsearchServiceMock } from '../../../../../src/core/server/mocks';
import { verifyApiAccess } from '../lib/license_api_access';
import { mockLicenseState } from '../lib/license_state.mock';
import { encryptedSavedObjectsMock } from '../../../encrypted_saved_objects/server/mocks';

jest.mock('../lib/license_api_access.ts', () => ({
verifyApiAccess: jest.fn(),
Expand All @@ -24,7 +25,9 @@ describe('healthRoute', () => {
const router: RouterMock = mockRouter.create();

const licenseState = mockLicenseState();
healthRoute(router, licenseState);
const encryptedSavedObjects = encryptedSavedObjectsMock.createSetup();
encryptedSavedObjects.usingEphemeralEncryptionKey = false;
healthRoute(router, licenseState, encryptedSavedObjects);

const [config] = router.get.mock.calls[0];

Expand All @@ -35,7 +38,9 @@ describe('healthRoute', () => {
const router: RouterMock = mockRouter.create();

const licenseState = mockLicenseState();
healthRoute(router, licenseState);
const encryptedSavedObjects = encryptedSavedObjectsMock.createSetup();
encryptedSavedObjects.usingEphemeralEncryptionKey = false;
healthRoute(router, licenseState, encryptedSavedObjects);
const [, handler] = router.get.mock.calls[0];

const elasticsearch = elasticsearchServiceMock.createSetup();
Expand All @@ -58,11 +63,37 @@ describe('healthRoute', () => {
`);
});

it('evaluates whether Encrypted Saved Objects is using an ephemeral encryption key', async () => {
const router: RouterMock = mockRouter.create();

const licenseState = mockLicenseState();
const encryptedSavedObjects = encryptedSavedObjectsMock.createSetup();
encryptedSavedObjects.usingEphemeralEncryptionKey = true;
healthRoute(router, licenseState, encryptedSavedObjects);
const [, handler] = router.get.mock.calls[0];

const elasticsearch = elasticsearchServiceMock.createSetup();
elasticsearch.adminClient.callAsInternalUser.mockReturnValue(Promise.resolve({}));

const [context, req, res] = mockHandlerArguments({ elasticsearch }, {}, ['ok']);

expect(await handler(context, req, res)).toMatchInlineSnapshot(`
Object {
"body": Object {
"hasPermanentEncryptionKey": false,
"isSufficientlySecure": true,
},
}
`);
});

it('evaluates missing security info from the usage api to mean that the security plugin is disbled', async () => {
const router: RouterMock = mockRouter.create();

const licenseState = mockLicenseState();
healthRoute(router, licenseState);
const encryptedSavedObjects = encryptedSavedObjectsMock.createSetup();
encryptedSavedObjects.usingEphemeralEncryptionKey = false;
healthRoute(router, licenseState, encryptedSavedObjects);
const [, handler] = router.get.mock.calls[0];

const elasticsearch = elasticsearchServiceMock.createSetup();
Expand All @@ -73,6 +104,7 @@ describe('healthRoute', () => {
expect(await handler(context, req, res)).toMatchInlineSnapshot(`
Object {
"body": Object {
"hasPermanentEncryptionKey": true,
"isSufficientlySecure": true,
},
}
Expand All @@ -83,7 +115,9 @@ describe('healthRoute', () => {
const router: RouterMock = mockRouter.create();

const licenseState = mockLicenseState();
healthRoute(router, licenseState);
const encryptedSavedObjects = encryptedSavedObjectsMock.createSetup();
encryptedSavedObjects.usingEphemeralEncryptionKey = false;
healthRoute(router, licenseState, encryptedSavedObjects);
const [, handler] = router.get.mock.calls[0];

const elasticsearch = elasticsearchServiceMock.createSetup();
Expand All @@ -94,6 +128,7 @@ describe('healthRoute', () => {
expect(await handler(context, req, res)).toMatchInlineSnapshot(`
Object {
"body": Object {
"hasPermanentEncryptionKey": true,
"isSufficientlySecure": true,
},
}
Expand All @@ -104,7 +139,9 @@ describe('healthRoute', () => {
const router: RouterMock = mockRouter.create();

const licenseState = mockLicenseState();
healthRoute(router, licenseState);
const encryptedSavedObjects = encryptedSavedObjectsMock.createSetup();
encryptedSavedObjects.usingEphemeralEncryptionKey = false;
healthRoute(router, licenseState, encryptedSavedObjects);
const [, handler] = router.get.mock.calls[0];

const elasticsearch = elasticsearchServiceMock.createSetup();
Expand All @@ -117,6 +154,7 @@ describe('healthRoute', () => {
expect(await handler(context, req, res)).toMatchInlineSnapshot(`
Object {
"body": Object {
"hasPermanentEncryptionKey": true,
"isSufficientlySecure": false,
},
}
Expand All @@ -127,7 +165,9 @@ describe('healthRoute', () => {
const router: RouterMock = mockRouter.create();

const licenseState = mockLicenseState();
healthRoute(router, licenseState);
const encryptedSavedObjects = encryptedSavedObjectsMock.createSetup();
encryptedSavedObjects.usingEphemeralEncryptionKey = false;
healthRoute(router, licenseState, encryptedSavedObjects);
const [, handler] = router.get.mock.calls[0];

const elasticsearch = elasticsearchServiceMock.createSetup();
Expand All @@ -140,6 +180,7 @@ describe('healthRoute', () => {
expect(await handler(context, req, res)).toMatchInlineSnapshot(`
Object {
"body": Object {
"hasPermanentEncryptionKey": true,
"isSufficientlySecure": false,
},
}
Expand All @@ -150,7 +191,9 @@ describe('healthRoute', () => {
const router: RouterMock = mockRouter.create();

const licenseState = mockLicenseState();
healthRoute(router, licenseState);
const encryptedSavedObjects = encryptedSavedObjectsMock.createSetup();
encryptedSavedObjects.usingEphemeralEncryptionKey = false;
healthRoute(router, licenseState, encryptedSavedObjects);
const [, handler] = router.get.mock.calls[0];

const elasticsearch = elasticsearchServiceMock.createSetup();
Expand All @@ -163,6 +206,7 @@ describe('healthRoute', () => {
expect(await handler(context, req, res)).toMatchInlineSnapshot(`
Object {
"body": Object {
"hasPermanentEncryptionKey": true,
"isSufficientlySecure": true,
},
}
Expand Down
8 changes: 7 additions & 1 deletion x-pack/plugins/alerting/server/routes/health.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
import { LicenseState } from '../lib/license_state';
import { verifyApiAccess } from '../lib/license_api_access';
import { AlertingFrameworkHealth } from '../types';
import { EncryptedSavedObjectsPluginSetup } from '../../../encrypted_saved_objects/server';

interface XPackUsageSecurity {
security?: {
Expand All @@ -26,7 +27,11 @@ interface XPackUsageSecurity {
};
}

export function healthRoute(router: IRouter, licenseState: LicenseState) {
export function healthRoute(
router: IRouter,
licenseState: LicenseState,
encryptedSavedObjects: EncryptedSavedObjectsPluginSetup
) {
router.get(
{
path: '/api/alert/_health',
Expand Down Expand Up @@ -54,6 +59,7 @@ export function healthRoute(router: IRouter, licenseState: LicenseState) {

const frameworkHealth: AlertingFrameworkHealth = {
isSufficientlySecure: !isSecurityEnabled || (isSecurityEnabled && isTLSEnabled),
hasPermanentEncryptionKey: !encryptedSavedObjects.usingEphemeralEncryptionKey,
};

return res.ok({
Expand Down
17 changes: 5 additions & 12 deletions x-pack/plugins/translations/translations/ja-JP.json
Original file line number Diff line number Diff line change
Expand Up @@ -15881,8 +15881,6 @@
"xpack.triggersActionsUI.common.expressionItems.threshold.andLabel": "AND",
"xpack.triggersActionsUI.common.expressionItems.threshold.descriptionLabel": "タイミング",
"xpack.triggersActionsUI.common.expressionItems.threshold.popoverTitle": "タイミング",
"xpack.triggersActionsUI.components.alertActionSecurityCallOut.enableTlsCta": "TLS を有効にする",
"xpack.triggersActionsUI.components.alertActionSecurityCallOut.tlsDisabledTitle": "アラート {action} を実行するには Elasticsearch と Kibana の間に TLS が必要です。",
"xpack.triggersActionsUI.components.builtinActionTypes.emailAction.actionTypeTitle": "メールに送信",
"xpack.triggersActionsUI.components.builtinActionTypes.emailAction.addVariablePopoverButton": "変数を追加",
"xpack.triggersActionsUI.components.builtinActionTypes.emailAction.selectMessageText": "サーバーからメールを送信します。",
Expand Down Expand Up @@ -15977,9 +15975,6 @@
"xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.viewHeadersSwitch": "HTTP ヘッダーを追加",
"xpack.triggersActionsUI.components.deleteSelectedIdsErrorNotification.descriptionText": "{numErrors, number} {numErrors, plural, one {{singleTitle}} other {{multipleTitle}}}を削除できませんでした",
"xpack.triggersActionsUI.components.deleteSelectedIdsSuccessNotification.descriptionText": "{numSuccesses, number} {numSuccesses, plural, one {{singleTitle}} other {{multipleTitle}}}を削除しました",
"xpack.triggersActionsUI.components.securityCallOut.enableTlsCta": "TLS を有効にする",
"xpack.triggersActionsUI.components.securityCallOut.tlsDisabledDescription": "アラートは API キー に依存し、キーを使用するには Elasticsearch と Kibana の間に TLS が必要です。",
"xpack.triggersActionsUI.components.securityCallOut.tlsDisabledTitle": "トランスポートレイヤーセキュリティを有効にする",
"xpack.triggersActionsUI.connectors.breadcrumbTitle": "コネクター",
"xpack.triggersActionsUI.deleteSelectedIdsConfirmModal.cancelButtonLabel": "キャンセル",
"xpack.triggersActionsUI.deleteSelectedIdsConfirmModal.deleteButtonLabel": "{numIdsToDelete, plural, one {{singleTitle}} other {# {multipleTitle}}}を削除 ",
Expand All @@ -16003,8 +15998,8 @@
"xpack.triggersActionsUI.sections.actionConnectorForm.error.requiredNameText": "名前が必要です。",
"xpack.triggersActionsUI.sections.actionForm.getMoreActionsTitle": "さらにアクションを表示",
"xpack.triggersActionsUI.sections.actionsConnectorsList.addActionButtonLabel": "コネクターを作成",
"xpack.triggersActionsUI.sections.actionsConnectorsList.addActionEmptyBody": "Kibana でトリガーできるメール、Slack, Elasticsearch、およびサードパーティサービスを構成します。",
"xpack.triggersActionsUI.sections.actionsConnectorsList.addActionEmptyTitle": "初めてのコネクターを作成する",
"xpack.triggersActionsUI.components.emptyConnectorsPrompt.addActionEmptyBody": "Kibana でトリガーできるメール、Slack, Elasticsearch、およびサードパーティサービスを構成します。",
"xpack.triggersActionsUI.components.emptyConnectorsPrompt.addActionEmptyTitle": "初めてのコネクターを作成する",
"xpack.triggersActionsUI.sections.actionsConnectorsList.buttons.deleteDisabledTitle": "コネクターを削除できません",
"xpack.triggersActionsUI.sections.actionsConnectorsList.buttons.deleteLabel": "{count} 件を削除",
"xpack.triggersActionsUI.sections.actionsConnectorsList.connectorsListTable.columns.actions.deleteActionDescription": "このコネクターを削除",
Expand Down Expand Up @@ -16054,7 +16049,6 @@
"xpack.triggersActionsUI.sections.alertAdd.saveButtonLabel": "保存",
"xpack.triggersActionsUI.sections.alertAdd.saveErrorNotificationText": "アラートを作成できません。",
"xpack.triggersActionsUI.sections.alertAdd.saveSuccessNotificationText": "「{alertName}」 を保存しました",
"xpack.triggersActionsUI.sections.alertAdd.securityCalloutAction": "作成",
"xpack.triggersActionsUI.sections.alertAdd.selectIndex": "インデックスを選択してください。",
"xpack.triggersActionsUI.sections.alertAdd.threshold.closeIndexPopoverLabel": "閉じる",
"xpack.triggersActionsUI.sections.alertAdd.threshold.fixErrorInExpressionBelowValidationMessage": "下の表現のエラーを修正してください。",
Expand Down Expand Up @@ -16091,7 +16085,6 @@
"xpack.triggersActionsUI.sections.alertEdit.saveButtonLabel": "保存",
"xpack.triggersActionsUI.sections.alertEdit.saveErrorNotificationText": "アラートを更新できません。",
"xpack.triggersActionsUI.sections.alertEdit.saveSuccessNotificationText": "「{alertName}」 を更新しました",
"xpack.triggersActionsUI.sections.alertEdit.securityCalloutAction": "編集中",
"xpack.triggersActionsUI.sections.alertForm.accordion.deleteIconAriaLabel": "削除",
"xpack.triggersActionsUI.sections.alertForm.actionDisabledTitle": "このアクションは無効です",
"xpack.triggersActionsUI.sections.alertForm.actionIdLabel": "{connectorInstance} コネクター",
Expand Down Expand Up @@ -16143,9 +16136,9 @@
"xpack.triggersActionsUI.sections.alertsList.collapsedItemActons.enableTitle": "有効にする",
"xpack.triggersActionsUI.sections.alertsList.collapsedItemActons.muteTitle": "ミュート",
"xpack.triggersActionsUI.sections.alertsList.collapsedItemActons.popoverButtonTitle": "アクション",
"xpack.triggersActionsUI.sections.alertsList.emptyButton": "アラートの作成",
"xpack.triggersActionsUI.sections.alertsList.emptyDesc": "トリガーが起きたときにメール、Slack、または別のコネクターを通してアラートを受信します。",
"xpack.triggersActionsUI.sections.alertsList.emptyTitle": "初めてのアラートを作成する",
"xpack.triggersActionsUI.components.emptyPrompt.emptyButton": "アラートの作成",
"xpack.triggersActionsUI.components.emptyPrompt.emptyDesc": "トリガーが起きたときにメール、Slack、または別のコネクターを通してアラートを受信します。",
"xpack.triggersActionsUI.components.emptyPrompt.emptyTitle": "初めてのアラートを作成する",
"xpack.triggersActionsUI.sections.alertsList.multipleTitle": "アラート",
"xpack.triggersActionsUI.sections.alertsList.searchPlaceholderTitle": "検索",
"xpack.triggersActionsUI.sections.alertsList.singleTitle": "アラート",
Expand Down
Loading