[Synthetics] Fix TLS alert recovery (#161254)

Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
Co-authored-by: Abdul Wahab Zahid <awahab07@yahoo.com>
This commit is contained in:
Shahzad 2023-07-07 18:01:29 +02:00 committed by GitHub
parent bed184b829
commit 0fe62fba0b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 217 additions and 132 deletions

View file

@ -131,6 +131,8 @@ export const getCertsRequestBody = ({
},
},
_source: [
'@timestamp',
'config_id',
'monitor.id',
'monitor.name',
'monitor.type',
@ -201,7 +203,9 @@ export const processCertsResult = (result: CertificatesResults): CertResult => {
not_before: notBefore,
common_name: commonName,
monitorName: ping?.monitor?.name,
configId: ping.config_id!,
monitorUrl: ping?.url?.full,
'@timestamp': ping['@timestamp'],
monitorType: ping?.monitor?.type,
locationName: ping?.observer?.geo?.name,
};

View file

@ -75,7 +75,7 @@ export const SyntheticsMonitorStatusTranslations = {
export const TlsTranslations = {
defaultActionMessage: i18n.translate('xpack.synthetics.rules.tls.defaultActionMessage', {
defaultMessage: `Detected TLS certificate {commonName} is {status} - Elastic Synthetics\n\nDetails:\n\n- Summary: {summary}\n- Common name: {commonName}\n- Issuer: {issuer}\n- Monitor: {monitorName} \n- Monitor URL: {monitorUrl} \n- Monitor type: {monitorType} \n- From: {locationName}`,
defaultMessage: `TLS certificate {commonName} {status} - Elastic Synthetics\n\nDetails:\n\n- Summary: {summary}\n- Common name: {commonName}\n- Issuer: {issuer}\n- Monitor: {monitorName} \n- Monitor URL: {monitorUrl} \n- Monitor type: {monitorType} \n- From: {locationName}`,
values: {
commonName: '{{context.commonName}}',
issuer: '{{context.issuer}}',
@ -88,11 +88,11 @@ export const TlsTranslations = {
},
}),
defaultRecoveryMessage: i18n.translate('xpack.synthetics.rules.tls.defaultRecoveryMessage', {
defaultMessage: `Alert for TLS certificate {commonName} from issuer {issuer} has recovered - Elastic Synthetics\n\nDetails:\n\n- Summary: {summary}\n- Common name: {commonName}\n- Issuer: {issuer}\n- Monitor: {monitorName} \n- Monitor URL: {monitorUrl} \n- Monitor type: {monitorType} \n- From: {locationName}`,
defaultMessage: `TLS alert for monitor "{monitorName}" has recovered - Elastic Synthetics\n\nDetails:\n\n- Summary: {summary}\n- New status : {newStatus}\n- Previous status: {previousStatus}\n- Monitor: {monitorName} \n- URL: {monitorUrl} \n- Monitor type: {monitorType} \n- From: {locationName}`,
values: {
commonName: '{{context.commonName}}',
issuer: '{{context.issuer}}',
summary: '{{context.summary}}',
previousStatus: '{{context.previousStatus}}',
newStatus: '{{context.newStatus}}',
monitorName: '{{context.monitorName}}',
monitorUrl: '{{{context.monitorUrl}}}',
monitorType: '{{context.monitorType}}',

View file

@ -34,6 +34,7 @@ export const CertType = t.intersection([
t.type({
monitors: t.array(CertMonitorType),
sha256: t.string,
configId: t.string,
}),
t.partial({
not_after: t.string,
@ -45,6 +46,7 @@ export const CertType = t.intersection([
monitorType: t.string,
monitorUrl: t.string,
locationName: t.string,
'@timestamp': t.string,
}),
]);

View file

@ -150,6 +150,7 @@ export const PingType = t.intersection([
docId: t.string,
}),
t.partial({
'@timestamp': t.string,
agent: AgentType,
container: t.partial({
id: t.string,

View file

@ -139,7 +139,9 @@ export class SyntheticsRunner {
}
assertResults(results: PromiseType<ReturnType<typeof syntheticsRun>>) {
console.log('Asserting results...');
Object.entries(results).forEach(([_journey, result]) => {
console.log(`Journey: ${_journey}, Status: ${result.status}`);
if (result.status !== 'succeeded') {
process.exitCode = 1;
process.exit();

View file

@ -65,6 +65,7 @@ describe('CertificateList', () => {
not_after: '2015-04-12T23:59:59.000Z',
not_before: '2015-04-09T00:00:00.000Z',
common_name: '*.badssl.com',
configId: 'uptime-advanced-http-tls',
},
],
}}

View file

@ -46,10 +46,6 @@ interface Props {
}
export const CertificateList: React.FC<Props> = ({ page, certificates, sort, onChange }) => {
const onTableChange = (newVal: Partial<Props>) => {
onChange(newVal.page as Page, newVal.sort as CertSort);
};
const pagination = {
pageIndex: page.index,
pageSize: page.size,
@ -105,7 +101,9 @@ export const CertificateList: React.FC<Props> = ({ page, certificates, sort, onC
columns={columns}
items={certificates?.certs ?? []}
pagination={pagination}
onChange={onTableChange}
onChange={(newVal) => {
onChange(newVal.page as Page, newVal.sort as CertSort);
}}
sorting={{
sort: {
field: sort.field,

View file

@ -9,9 +9,10 @@ import React from 'react';
import moment from 'moment';
import { FingerprintCol } from './fingerprint_col';
import { render } from '../../utils/testing';
import { Cert } from '../../../../../common/runtime_types';
describe('FingerprintCol', () => {
const cert = {
const cert: Cert = {
monitors: [{ name: '', id: 'github', url: 'https://github.com/' }],
not_after: '2020-05-08T00:00:00.000Z',
not_before: '2018-05-08T00:00:00.000Z',
@ -19,6 +20,7 @@ describe('FingerprintCol', () => {
sha1: 'ca06f56b258b7a0d4f2b05470939478651151984'.toUpperCase(),
sha256: '3111500c4a66012cdae333ec3fca1c9dde45c954440e7ee413716bff3663c074'.toUpperCase(),
common_name: 'github.com',
configId: '123',
};
it('renders expected elements for valid props', async () => {

View file

@ -10,6 +10,8 @@ import { IBasePath } from '@kbn/core-http-server';
import { LocatorPublic } from '@kbn/share-plugin/common';
import { AlertsLocatorParams, getAlertUrl } from '@kbn/observability-plugin/common';
import { RuleExecutorServices } from '@kbn/alerting-plugin/server';
import { i18n } from '@kbn/i18n';
import { TLSLatestPing } from './tls_rule_executor';
import { ALERT_DETAILS_URL } from '../action_variables';
import { Cert } from '../../../common/runtime_types';
import { tlsTranslations } from '../translations';
@ -18,10 +20,10 @@ interface TLSContent {
status?: string;
}
const getValidBefore = ({ not_before: date }: Cert): TLSContent => {
if (!date) return { summary: 'Error, missing `certificate_not_valid_before` date.' };
const relativeDate = moment().diff(date, 'days');
const formattedDate = moment(date).format('MMM D, YYYY z');
const getValidBefore = (notBefore?: string): TLSContent => {
if (!notBefore) return { summary: 'Error, missing `certificate_not_valid_before` date.' };
const relativeDate = moment().diff(notBefore, 'days');
const formattedDate = moment(notBefore).format('MMM D, YYYY z');
return relativeDate >= 0
? {
summary: tlsTranslations.validBeforeExpiredString(formattedDate, relativeDate),
@ -32,10 +34,10 @@ const getValidBefore = ({ not_before: date }: Cert): TLSContent => {
status: tlsTranslations.invalidLabel,
};
};
const getValidAfter = ({ not_after: date }: Cert): TLSContent => {
if (!date) return { summary: 'Error, missing `certificate_not_valid_after` date.' };
const relativeDate = moment().diff(date, 'days');
const formattedDate = moment(date).format('MMM D, YYYY z');
const getValidAfter = (notAfter?: string): TLSContent => {
if (!notAfter) return { summary: 'Error, missing `certificate_not_valid_after` date.' };
const relativeDate = moment().diff(notAfter, 'days');
const formattedDate = moment(notAfter).format('MMM D, YYYY z');
return relativeDate >= 0
? {
summary: tlsTranslations.validAfterExpiredString(formattedDate, relativeDate),
@ -46,35 +48,36 @@ const getValidAfter = ({ not_after: date }: Cert): TLSContent => {
status: tlsTranslations.expiringLabel,
};
};
const mapCertsToSummaryString = (
cert: Cert,
certLimitMessage: (cert: Cert) => TLSContent
): TLSContent => certLimitMessage(cert);
export const getCertSummary = (cert: Cert, expirationThreshold: number, ageThreshold: number) => {
const isExpiring = new Date(cert.not_after ?? '').valueOf() < expirationThreshold;
const isAging = new Date(cert.not_before ?? '').valueOf() < ageThreshold;
let content: TLSContent | null = null;
if (isExpiring) {
content = mapCertsToSummaryString(cert, getValidAfter);
content = getValidAfter(cert.not_after);
} else if (isAging) {
content = mapCertsToSummaryString(cert, getValidBefore);
content = getValidBefore(cert.not_before);
}
const { summary = '', status = '' } = content || {};
return {
summary,
status,
sha256: cert.sha256 ?? '',
commonName: cert.common_name ?? '',
issuer: cert.issuer ?? '',
monitorName: cert.monitorName,
monitorType: cert.monitorType,
locationName: cert.locationName,
monitorUrl: cert.monitorUrl,
configId: cert.configId,
};
};
export const setRecoveredAlertsContext = async ({
type CertSummary = ReturnType<typeof getCertSummary>;
export const setTLSRecoveredAlertsContext = async ({
alertFactory,
basePath,
defaultStartedAt,
@ -82,6 +85,7 @@ export const setRecoveredAlertsContext = async ({
spaceId,
alertsLocator,
getAlertUuid,
latestPings,
}: {
alertFactory: RuleExecutorServices['alertFactory'];
defaultStartedAt: string;
@ -90,6 +94,7 @@ export const setRecoveredAlertsContext = async ({
spaceId: string;
alertsLocator?: LocatorPublic<AlertsLocatorParams>;
getAlertUuid?: (alertId: string) => string | null;
latestPings: TLSLatestPing[];
}) => {
const { getRecoveredAlerts } = alertFactory.done();
@ -98,7 +103,7 @@ export const setRecoveredAlertsContext = async ({
const alertUuid = getAlertUuid?.(recoveredAlertId) || null;
const indexedStartedAt = getAlertStartedDate(recoveredAlertId) ?? defaultStartedAt;
const state = alert.getState();
const state = alert.getState() as CertSummary;
const alertUrl = await getAlertUrl(
alertUuid,
spaceId,
@ -107,8 +112,43 @@ export const setRecoveredAlertsContext = async ({
basePath.publicBaseUrl
);
const configId = state.configId;
const latestPing = latestPings.find((ping) => ping.config_id === configId);
const previousStatus = i18n.translate('xpack.synthetics.alerts.tls.previousStatus', {
defaultMessage: 'Certificate {commonName} {summary}',
values: { commonName: state.commonName, summary: state.summary },
});
const newCommonName = latestPing?.tls?.server?.x509?.subject.common_name ?? '';
const newExpiryDate = latestPing?.tls?.server?.x509?.not_after ?? '';
const { summary } = getValidAfter(newExpiryDate);
let newStatus = i18n.translate('xpack.synthetics.alerts.tls.newStatus', {
defaultMessage: 'Certificate {commonName} {summary}',
values: { commonName: newCommonName, summary },
});
let newSummary = '';
if (state.sha256 !== latestPing?.tls?.server?.hash?.sha256) {
newSummary = i18n.translate('xpack.synthetics.alerts.tls.newSummary', {
defaultMessage: 'Monitor certificate has been updated.',
});
}
if (state.sha256 === latestPing?.tls?.server?.hash?.sha256 || !latestPing) {
// in this case it seems like threshold has been changed, but the cert is the same
newSummary = i18n.translate('xpack.synthetics.alerts.tls.newSummaryThreshold', {
defaultMessage: 'Expiry/Age threshold has been updated.',
});
newStatus = previousStatus;
}
alert.setContext({
...state,
newStatus,
previousStatus,
summary: newSummary,
[ALERT_DETAILS_URL]: alertUrl,
});
}

View file

@ -24,7 +24,7 @@ import {
CERT_VALID_NOT_AFTER,
CERT_VALID_NOT_BEFORE,
} from '../../../common/field_names';
import { getCertSummary, setRecoveredAlertsContext } from './message_utils';
import { getCertSummary, setTLSRecoveredAlertsContext } from './message_utils';
import { SyntheticsCommonState } from '../../../common/runtime_types/alert_rules/common';
import { TLSRuleExecutor } from './tls_rule_executor';
import {
@ -90,53 +90,51 @@ export const registerSyntheticsTLSCheckRule = (
syntheticsMonitorClient
);
const { foundCerts, certs, absoluteExpirationThreshold, absoluteAgeThreshold } =
const { foundCerts, certs, absoluteExpirationThreshold, absoluteAgeThreshold, latestPings } =
await tlsRule.getExpiredCertificates();
if (foundCerts) {
await asyncForEach(certs, async (cert) => {
const summary = getCertSummary(cert, absoluteExpirationThreshold, absoluteAgeThreshold);
await asyncForEach(certs, async (cert) => {
const summary = getCertSummary(cert, absoluteExpirationThreshold, absoluteAgeThreshold);
if (!summary.summary || !summary.status) {
return;
}
if (!summary.summary || !summary.status) {
return;
}
const alertId = `${cert.common_name}-${cert.issuer?.replace(/\s/g, '_')}-${cert.sha256}`;
const alertUuid = getAlertUuid(alertId);
const indexedStartedAt = getAlertStartedDate(alertId) ?? startedAt.toISOString();
const alertId = cert.sha256;
const alertUuid = getAlertUuid(alertId);
const indexedStartedAt = getAlertStartedDate(alertId) ?? startedAt.toISOString();
const alertInstance = alertWithLifecycle({
id: alertId,
fields: {
[CERT_COMMON_NAME]: cert.common_name,
[CERT_ISSUER_NAME]: cert.issuer,
[CERT_VALID_NOT_AFTER]: cert.not_after,
[CERT_VALID_NOT_BEFORE]: cert.not_before,
[CERT_HASH_SHA256]: cert.sha256,
[ALERT_UUID]: alertUuid,
[ALERT_REASON]: generateAlertMessage(TlsTranslations.defaultActionMessage, summary),
},
});
alertInstance.replaceState({
...updateState(ruleState, foundCerts),
...summary,
});
alertInstance.scheduleActions(TLS_CERTIFICATE.id, {
[ALERT_DETAILS_URL]: await getAlertUrl(
alertUuid,
spaceId,
indexedStartedAt,
alertsLocator,
basePath.publicBaseUrl
),
...summary,
});
const alertInstance = alertWithLifecycle({
id: alertId,
fields: {
[CERT_COMMON_NAME]: cert.common_name,
[CERT_ISSUER_NAME]: cert.issuer,
[CERT_VALID_NOT_AFTER]: cert.not_after,
[CERT_VALID_NOT_BEFORE]: cert.not_before,
[CERT_HASH_SHA256]: cert.sha256,
[ALERT_UUID]: alertUuid,
[ALERT_REASON]: generateAlertMessage(TlsTranslations.defaultActionMessage, summary),
},
});
}
await setRecoveredAlertsContext({
alertInstance.replaceState({
...updateState(ruleState, foundCerts),
...summary,
});
alertInstance.scheduleActions(TLS_CERTIFICATE.id, {
[ALERT_DETAILS_URL]: await getAlertUrl(
alertUuid,
spaceId,
indexedStartedAt,
alertsLocator,
basePath.publicBaseUrl
),
...summary,
});
});
await setTLSRecoveredAlertsContext({
alertFactory,
basePath,
defaultStartedAt: startedAt.toISOString(),
@ -144,6 +142,7 @@ export const registerSyntheticsTLSCheckRule = (
getAlertUuid,
spaceId,
alertsLocator,
latestPings,
});
return { state: updateState(ruleState, foundCerts) };

View file

@ -11,6 +11,7 @@ import {
import { ElasticsearchClient } from '@kbn/core-elasticsearch-server';
import { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/types';
import moment from 'moment';
import { SUMMARY_FILTER } from '../../../common/constants/client_defaults';
import { formatFilterString } from '../common';
import { SyntheticsServerSetup } from '../../types';
import { getSyntheticsCerts } from '../../queries/get_certs';
@ -21,7 +22,12 @@ import {
getAllMonitors,
processMonitors,
} from '../../saved_objects/synthetics_monitor/get_all_monitors';
import { CertResult, ConfigKey, EncryptedSyntheticsMonitor } from '../../../common/runtime_types';
import {
CertResult,
ConfigKey,
EncryptedSyntheticsMonitor,
Ping,
} from '../../../common/runtime_types';
import { SyntheticsMonitorClient } from '../../synthetics_service/synthetics_monitor/synthetics_monitor_client';
import { monitorAttributes } from '../../../common/types/saved_objects';
import { AlertConfigKey } from '../../../common/constants/monitor_management';
@ -105,6 +111,7 @@ export class TLSRuleExecutor {
if (enabledMonitorQueryIds.length === 0) {
return {
latestPings: [],
certs: [],
total: 0,
foundCerts: false,
@ -133,16 +140,90 @@ export class TLSRuleExecutor {
monitorIds: enabledMonitorQueryIds,
});
const latestPings = await this.getLatestPingsForMonitors(certs);
const foundCerts = total > 0;
return {
latestPings,
foundCerts,
certs,
total,
expiryThreshold,
ageThreshold,
absoluteExpirationThreshold,
absoluteAgeThreshold,
certs: this.filterOutResolvedCerts(certs, latestPings),
};
}
filterOutResolvedCerts(certs: CertResult['certs'], latestPings: TLSLatestPing[]) {
const latestPingsMap = new Map<string, TLSLatestPing>();
latestPings.forEach((ping) => {
latestPingsMap.set(ping.config_id!, ping);
});
return certs.filter((cert) => {
const lPing = latestPingsMap.get(cert.configId);
if (!lPing) {
return true;
}
return moment(lPing['@timestamp']).isBefore(cert['@timestamp']);
});
}
async getLatestPingsForMonitors(certs: CertResult['certs']) {
if (certs.length === 0) {
return [];
}
const configIds = certs.map((cert) => cert.configId);
const certIds = certs.map((cert) => cert.sha256);
const { body } = await this.esClient.search({
body: {
query: {
bool: {
filter: [
{
range: {
'@timestamp': {
gte: 'now-1d',
lt: 'now',
},
},
},
{
terms: {
config_id: configIds,
},
},
SUMMARY_FILTER,
],
must_not: {
bool: {
filter: [
{
terms: {
'tls.server.hash.sha256': certIds,
},
},
],
},
},
},
},
collapse: {
field: 'config_id',
},
_source: ['@timestamp', 'monitor', 'url', 'config_id', 'tls'],
sort: [
{
'@timestamp': {
order: 'desc',
},
},
],
},
});
return body.hits.hits.map((hit) => hit._source as TLSLatestPing);
}
}
export type TLSLatestPing = Pick<Ping, '@timestamp' | 'monitor' | 'url' | 'tls' | 'config_id'>;

View file

@ -166,47 +166,6 @@ export const commonStateTranslations = [
];
export const tlsTranslations = {
actionVariables: [
{
name: 'count',
description: i18n.translate('xpack.synthetics.rules.tls.actionVariables.state.count', {
defaultMessage: 'The number of certs detected by the alert executor',
}),
},
{
name: 'expiringCount',
description: i18n.translate(
'xpack.synthetics.rules.tls.actionVariables.state.expiringCount',
{
defaultMessage: 'The number of expiring certs detected by the alert.',
}
),
},
{
name: 'expiringCommonNameAndDate',
description: i18n.translate(
'xpack.synthetics.rules.tls.actionVariables.state.expiringCommonNameAndDate',
{
defaultMessage: 'The common names and expiration date/time of the detected certs',
}
),
},
{
name: 'agingCount',
description: i18n.translate('xpack.synthetics.rules.tls.actionVariables.state.agingCount', {
defaultMessage: 'The number of detected certs that are becoming too old.',
}),
},
{
name: 'agingCommonNameAndDate',
description: i18n.translate(
'xpack.synthetics.rules.tls.actionVariables.state.agingCommonNameAndDate',
{
defaultMessage: 'The common names and expiration date/time of the detected certs.',
}
),
},
],
validAfterExpiredString: (date: string, relativeDate: number) =>
i18n.translate('xpack.synthetics.rules.tls.validAfterExpiredString', {
defaultMessage: `Expired on {date}, {relativeDate} days ago.`,
@ -225,7 +184,7 @@ export const tlsTranslations = {
}),
validBeforeExpiredString: (date: string, relativeDate: number) =>
i18n.translate('xpack.synthetics.rules.tls.validBeforeExpiredString', {
defaultMessage: 'valid since {date}, {relativeDate} days ago.',
defaultMessage: 'Valid since {date}, {relativeDate} days ago.',
values: {
date,
relativeDate,
@ -233,22 +192,22 @@ export const tlsTranslations = {
}),
validBeforeExpiringString: (date: string, relativeDate: number) =>
i18n.translate('xpack.synthetics.rules.tls.validBeforeExpiringString', {
defaultMessage: 'invalid until {date}, {relativeDate} days from now.',
defaultMessage: 'Invalid until {date}, {relativeDate} days from now.',
values: {
date,
relativeDate,
},
}),
expiredLabel: i18n.translate('xpack.synthetics.rules.tls.expiredLabel', {
defaultMessage: 'expired',
defaultMessage: 'has expired',
}),
expiringLabel: i18n.translate('xpack.synthetics.rules.tls.expiringLabel', {
defaultMessage: 'expiring',
defaultMessage: 'is expiring soon',
}),
agingLabel: i18n.translate('xpack.synthetics.rules.tls.agingLabel', {
defaultMessage: 'becoming too old',
defaultMessage: 'is becoming too old',
}),
invalidLabel: i18n.translate('xpack.synthetics.rules.tls.invalidLabel', {
defaultMessage: 'invalid',
defaultMessage: 'is invalid',
}),
};

View file

@ -9,6 +9,7 @@ import expect from '@kbn/expect';
import { API_URLS, SYNTHETICS_API_URLS } from '@kbn/synthetics-plugin/common/constants';
import { SanitizedRule } from '@kbn/alerting-plugin/common';
import { omit } from 'lodash';
import { TlsTranslations } from '@kbn/synthetics-plugin/common/rules/synthetics/translations';
import { FtrProviderContext } from '../common/ftr_provider_context';
// eslint-disable-next-line import/no-default-export
@ -359,7 +360,7 @@ const tlsRule = {
{
group: 'recovered',
params: {
body: 'Alert for TLS certificate {{context.commonName}} from issuer {{context.issuer}} has recovered - Elastic Synthetics\n\nDetails:\n\n- Summary: {{context.summary}}\n- Common name: {{context.commonName}}\n- Issuer: {{context.issuer}}\n- Monitor: {{context.monitorName}} \n- Monitor URL: {{{context.monitorUrl}}} \n- Monitor type: {{context.monitorType}} \n- From: {{context.locationName}}',
body: TlsTranslations.defaultRecoveryMessage,
},
frequency: { notifyWhen: 'onActionGroupChange', throttle: null, summary: false },
uuid: '52070ef7-c288-40e7-ae5b-51c7d77463cb',
@ -369,7 +370,7 @@ const tlsRule = {
{
group: 'xpack.synthetics.alerts.actionGroups.tls',
params: {
body: 'Detected TLS certificate {{context.commonName}} is {{context.status}} - Elastic Synthetics\n\nDetails:\n\n- Summary: {{context.summary}}\n- Common name: {{context.commonName}}\n- Issuer: {{context.issuer}}\n- Monitor: {{context.monitorName}} \n- Monitor URL: {{{context.monitorUrl}}} \n- Monitor type: {{context.monitorType}} \n- From: {{context.locationName}}',
body: TlsTranslations.defaultActionMessage,
},
frequency: { notifyWhen: 'onActionGroupChange', throttle: null, summary: false },
uuid: '4d003e7b-e37d-47e6-8ee6-2d80b61fa31f',
@ -381,8 +382,7 @@ const tlsRule = {
params: {
to: ['test@gmail.com'],
subject: 'Alert has resolved for certificate {{context.commonName}} - Elastic Synthetics',
message:
'Alert for TLS certificate {{context.commonName}} from issuer {{context.issuer}} has recovered - Elastic Synthetics\n\nDetails:\n\n- Summary: {{context.summary}}\n- Common name: {{context.commonName}}\n- Issuer: {{context.issuer}}\n- Monitor: {{context.monitorName}} \n- Monitor URL: {{{context.monitorUrl}}} \n- Monitor type: {{context.monitorType}} \n- From: {{context.locationName}}',
message: TlsTranslations.defaultRecoveryMessage,
messageHTML: null,
cc: [],
bcc: [],
@ -398,8 +398,7 @@ const tlsRule = {
params: {
to: ['test@gmail.com'],
subject: 'Alert triggered for certificate {{context.commonName}} - Elastic Synthetics',
message:
'Detected TLS certificate {{context.commonName}} is {{context.status}} - Elastic Synthetics\n\nDetails:\n\n- Summary: {{context.summary}}\n- Common name: {{context.commonName}}\n- Issuer: {{context.issuer}}\n- Monitor: {{context.monitorName}} \n- Monitor URL: {{{context.monitorUrl}}} \n- Monitor type: {{context.monitorType}} \n- From: {{context.locationName}}',
message: TlsTranslations.defaultActionMessage,
messageHTML: null,
cc: [],
bcc: [],
@ -455,10 +454,8 @@ const tlsRule = {
subAction: 'pushToService',
subActionParams: {
incident: {
short_description:
'Detected TLS certificate {{context.commonName}} is {{context.status}} - Elastic Synthetics\n\nDetails:\n\n- Summary: {{context.summary}}\n- Common name: {{context.commonName}}\n- Issuer: {{context.issuer}}\n- Monitor: {{context.monitorName}} \n- Monitor URL: {{{context.monitorUrl}}} \n- Monitor type: {{context.monitorType}} \n- From: {{context.locationName}}',
description:
'Detected TLS certificate {{context.commonName}} is {{context.status}} - Elastic Synthetics\n\nDetails:\n\n- Summary: {{context.summary}}\n- Common name: {{context.commonName}}\n- Issuer: {{context.issuer}}\n- Monitor: {{context.monitorName}} \n- Monitor URL: {{{context.monitorUrl}}} \n- Monitor type: {{context.monitorType}} \n- From: {{context.locationName}}',
short_description: TlsTranslations.defaultActionMessage,
description: TlsTranslations.defaultActionMessage,
impact: '2',
severity: '2',
urgency: '2',
@ -479,8 +476,7 @@ const tlsRule = {
{
group: 'recovered',
params: {
message:
'Alert for TLS certificate {{context.commonName}} from issuer {{context.issuer}} has recovered - Elastic Synthetics\n\nDetails:\n\n- Summary: {{context.summary}}\n- Common name: {{context.commonName}}\n- Issuer: {{context.issuer}}\n- Monitor: {{context.monitorName}} \n- Monitor URL: {{{context.monitorUrl}}} \n- Monitor type: {{context.monitorType}} \n- From: {{context.locationName}}',
message: TlsTranslations.defaultRecoveryMessage,
},
frequency: { notifyWhen: 'onActionGroupChange', throttle: null, summary: false },
uuid: '25822900-a030-4e59-b9c7-909ff665a862',
@ -490,8 +486,7 @@ const tlsRule = {
{
group: 'xpack.synthetics.alerts.actionGroups.tls',
params: {
message:
'Detected TLS certificate {{context.commonName}} is {{context.status}} - Elastic Synthetics\n\nDetails:\n\n- Summary: {{context.summary}}\n- Common name: {{context.commonName}}\n- Issuer: {{context.issuer}}\n- Monitor: {{context.monitorName}} \n- Monitor URL: {{{context.monitorUrl}}} \n- Monitor type: {{context.monitorType}} \n- From: {{context.locationName}}',
message: TlsTranslations.defaultActionMessage,
},
frequency: { notifyWhen: 'onActionGroupChange', throttle: null, summary: false },
uuid: '07896abe-5ebe-4e7f-95e4-3944e6831843',

View file

@ -8,12 +8,12 @@
import expect from '@kbn/expect';
import moment from 'moment';
import { isRight } from 'fp-ts/lib/Either';
import { CertType } from '@kbn/synthetics-plugin/common/runtime_types';
import {
processCertsResult,
getCertsRequestBody,
} from '@kbn/synthetics-plugin/common/requests/get_certs_request_body';
import { ELASTIC_HTTP_VERSION_HEADER } from '@kbn/core-http-common';
import { CertType } from '@kbn/uptime-plugin/common/runtime_types';
import { FtrProviderContext } from '../../../ftr_provider_context';
import { makeChecksWithStatus } from './helper/make_checks';

View file

@ -133,6 +133,7 @@
"@kbn/logs-shared-plugin",
"@kbn/telemetry-tools",
"@kbn/profiling-plugin",
"@kbn/observability-onboarding-plugin"
"@kbn/observability-onboarding-plugin",
"@kbn/uptime-plugin"
]
}