@oneuptime/common 10.4.12 → 10.4.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/Server/Utils/Monitor/Criteria/DnssecMonitorCriteria.ts +108 -0
  2. package/Server/Utils/Monitor/MonitorCriteriaEvaluator.ts +13 -0
  3. package/Server/Utils/Monitor/MonitorTemplateUtil.ts +25 -0
  4. package/Types/Monitor/CriteriaFilter.ts +13 -0
  5. package/Types/Monitor/DnssecMonitor/DnssecMonitorResponse.ts +69 -0
  6. package/Types/Monitor/MonitorCriteriaInstance.ts +67 -0
  7. package/Types/Monitor/MonitorStep.ts +39 -0
  8. package/Types/Monitor/MonitorStepDnssecMonitor.ts +59 -0
  9. package/Types/Monitor/MonitorType.ts +17 -1
  10. package/Types/Probe/ProbeMonitorResponse.ts +2 -0
  11. package/UI/Components/MonitorTemplateVariables/TemplateVariablesCatalog.ts +51 -0
  12. package/Utils/Monitor/MonitorMetricType.ts +1 -0
  13. package/build/dist/Server/Utils/Monitor/Criteria/DnssecMonitorCriteria.js +94 -0
  14. package/build/dist/Server/Utils/Monitor/Criteria/DnssecMonitorCriteria.js.map +1 -0
  15. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js +10 -0
  16. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js.map +1 -1
  17. package/build/dist/Server/Utils/Monitor/MonitorTemplateUtil.js +22 -3
  18. package/build/dist/Server/Utils/Monitor/MonitorTemplateUtil.js.map +1 -1
  19. package/build/dist/Types/Monitor/CriteriaFilter.js +12 -0
  20. package/build/dist/Types/Monitor/CriteriaFilter.js.map +1 -1
  21. package/build/dist/Types/Monitor/DnssecMonitor/DnssecMonitorResponse.js +2 -0
  22. package/build/dist/Types/Monitor/DnssecMonitor/DnssecMonitorResponse.js.map +1 -0
  23. package/build/dist/Types/Monitor/MonitorCriteriaInstance.js +62 -0
  24. package/build/dist/Types/Monitor/MonitorCriteriaInstance.js.map +1 -1
  25. package/build/dist/Types/Monitor/MonitorStep.js +26 -0
  26. package/build/dist/Types/Monitor/MonitorStep.js.map +1 -1
  27. package/build/dist/Types/Monitor/MonitorStepDnssecMonitor.js +42 -0
  28. package/build/dist/Types/Monitor/MonitorStepDnssecMonitor.js.map +1 -0
  29. package/build/dist/Types/Monitor/MonitorType.js +15 -1
  30. package/build/dist/Types/Monitor/MonitorType.js.map +1 -1
  31. package/build/dist/UI/Components/MonitorTemplateVariables/TemplateVariablesCatalog.js +47 -0
  32. package/build/dist/UI/Components/MonitorTemplateVariables/TemplateVariablesCatalog.js.map +1 -1
  33. package/build/dist/Utils/Monitor/MonitorMetricType.js +1 -0
  34. package/build/dist/Utils/Monitor/MonitorMetricType.js.map +1 -1
  35. package/package.json +1 -1
@@ -0,0 +1,108 @@
1
+ import DataToProcess from "../DataToProcess";
2
+ import CompareCriteria from "./CompareCriteria";
3
+ import {
4
+ CheckOn,
5
+ CriteriaFilter,
6
+ FilterType,
7
+ } from "../../../../Types/Monitor/CriteriaFilter";
8
+ import DnssecMonitorResponse from "../../../../Types/Monitor/DnssecMonitor/DnssecMonitorResponse";
9
+ import ProbeMonitorResponse from "../../../../Types/Probe/ProbeMonitorResponse";
10
+ import CaptureSpan from "../../Telemetry/CaptureSpan";
11
+
12
+ export default class DnssecMonitorCriteria {
13
+ @CaptureSpan()
14
+ public static async isMonitorInstanceCriteriaFilterMet(input: {
15
+ dataToProcess: DataToProcess;
16
+ criteriaFilter: CriteriaFilter;
17
+ }): Promise<string | null> {
18
+ const dataToProcess: ProbeMonitorResponse =
19
+ input.dataToProcess as ProbeMonitorResponse;
20
+
21
+ const dnssecResponse: DnssecMonitorResponse | undefined =
22
+ dataToProcess.dnssecResponse;
23
+
24
+ if (!dnssecResponse) {
25
+ return null;
26
+ }
27
+
28
+ const isTrue: boolean = input.criteriaFilter.filterType === FilterType.True;
29
+ const isFalse: boolean =
30
+ input.criteriaFilter.filterType === FilterType.False;
31
+
32
+ if (input.criteriaFilter.checkOn === CheckOn.DnssecChainValid) {
33
+ if (dnssecResponse.isChainValid && isTrue) {
34
+ return `DNSSEC chain is valid for ${dnssecResponse.domainName}.`;
35
+ }
36
+ if (!dnssecResponse.isChainValid && isFalse) {
37
+ return `DNSSEC chain validation failed for ${dnssecResponse.domainName}.`;
38
+ }
39
+ return null;
40
+ }
41
+
42
+ if (input.criteriaFilter.checkOn === CheckOn.DnssecDnskeyExists) {
43
+ const exists: boolean = dnssecResponse.dnskeys.length > 0;
44
+ if (exists && isTrue) {
45
+ return `DNSKEY records present for ${dnssecResponse.domainName}.`;
46
+ }
47
+ if (!exists && isFalse) {
48
+ return `No DNSKEY records found for ${dnssecResponse.domainName}.`;
49
+ }
50
+ return null;
51
+ }
52
+
53
+ if (input.criteriaFilter.checkOn === CheckOn.DnssecDsExists) {
54
+ const exists: boolean = dnssecResponse.isParentDsPresent;
55
+ if (exists && isTrue) {
56
+ return `DS records present at parent zone for ${dnssecResponse.domainName}.`;
57
+ }
58
+ if (!exists && isFalse) {
59
+ return `No DS records found at the parent zone for ${dnssecResponse.domainName}.`;
60
+ }
61
+ return null;
62
+ }
63
+
64
+ if (input.criteriaFilter.checkOn === CheckOn.DnssecResolverConsensus) {
65
+ const consensus: boolean = dnssecResponse.resolverConsensusAd;
66
+ if (consensus && isTrue) {
67
+ return `All resolvers report DNSSEC-valid (AD flag) for ${dnssecResponse.domainName}.`;
68
+ }
69
+ if (!consensus && isFalse) {
70
+ return `Resolvers do not agree on DNSSEC validity for ${dnssecResponse.domainName}.`;
71
+ }
72
+ return null;
73
+ }
74
+
75
+ if (input.criteriaFilter.checkOn === CheckOn.DnssecNameserverConsistent) {
76
+ const consistent: boolean = dnssecResponse.isNameserverConsistent;
77
+ if (consistent && isTrue) {
78
+ return `Authoritative nameservers are consistent for ${dnssecResponse.domainName}.`;
79
+ }
80
+ if (!consistent && isFalse) {
81
+ return `Authoritative nameservers are inconsistent for ${dnssecResponse.domainName}.`;
82
+ }
83
+ return null;
84
+ }
85
+
86
+ if (input.criteriaFilter.checkOn === CheckOn.DnssecSignatureExpiresInDays) {
87
+ const threshold: number | null = CompareCriteria.convertToNumber(
88
+ input.criteriaFilter.value,
89
+ );
90
+
91
+ if (threshold === null || threshold === undefined) {
92
+ return null;
93
+ }
94
+
95
+ if (dnssecResponse.daysUntilSignatureExpiry === undefined) {
96
+ return null;
97
+ }
98
+
99
+ return CompareCriteria.compareCriteriaNumbers({
100
+ value: dnssecResponse.daysUntilSignatureExpiry,
101
+ threshold: threshold,
102
+ criteriaFilter: input.criteriaFilter,
103
+ });
104
+ }
105
+
106
+ return null;
107
+ }
108
+ }
@@ -18,6 +18,7 @@ import ProfileMonitorCriteria from "./Criteria/ProfileMonitorCriteria";
18
18
  import SnmpMonitorCriteria from "./Criteria/SnmpMonitorCriteria";
19
19
  import DnsMonitorCriteria from "./Criteria/DnsMonitorCriteria";
20
20
  import DomainMonitorCriteria from "./Criteria/DomainMonitorCriteria";
21
+ import DnssecMonitorCriteria from "./Criteria/DnssecMonitorCriteria";
21
22
  import ExternalStatusPageMonitorCriteria from "./Criteria/ExternalStatusPageMonitorCriteria";
22
23
  import MonitorCriteriaMessageBuilder from "./MonitorCriteriaMessageBuilder";
23
24
  import MonitorCriteriaDataExtractor from "./MonitorCriteriaDataExtractor";
@@ -761,6 +762,18 @@ ${contextBlock}
761
762
  }
762
763
  }
763
764
 
765
+ if (input.monitor.monitorType === MonitorType.DNSSEC) {
766
+ const dnssecMonitorResult: string | null =
767
+ await DnssecMonitorCriteria.isMonitorInstanceCriteriaFilterMet({
768
+ dataToProcess: input.dataToProcess,
769
+ criteriaFilter: input.criteriaFilter,
770
+ });
771
+
772
+ if (dnssecMonitorResult) {
773
+ return dnssecMonitorResult;
774
+ }
775
+ }
776
+
764
777
  if (input.monitor.monitorType === MonitorType.ExternalStatusPage) {
765
778
  const externalStatusPageResult: string | null =
766
779
  await ExternalStatusPageMonitorCriteria.isMonitorInstanceCriteriaFilterMet(
@@ -19,6 +19,7 @@ import DnsMonitorResponse, {
19
19
  DnsRecordResponse,
20
20
  } from "../../../Types/Monitor/DnsMonitor/DnsMonitorResponse";
21
21
  import DomainMonitorResponse from "../../../Types/Monitor/DomainMonitor/DomainMonitorResponse";
22
+ import DnssecMonitorResponse from "../../../Types/Monitor/DnssecMonitor/DnssecMonitorResponse";
22
23
  import ExternalStatusPageMonitorResponse, {
23
24
  ExternalStatusPageComponentStatus,
24
25
  } from "../../../Types/Monitor/ExternalStatusPageMonitor/ExternalStatusPageMonitorResponse";
@@ -332,6 +333,30 @@ export default class MonitorTemplateUtil {
332
333
  } as JSONObject;
333
334
  }
334
335
 
336
+ if (data.monitorType === MonitorType.DNSSEC) {
337
+ const dnssecResponse: DnssecMonitorResponse | undefined = (
338
+ data.dataToProcess as ProbeMonitorResponse
339
+ ).dnssecResponse;
340
+
341
+ storageMap = {
342
+ isOnline: (data.dataToProcess as ProbeMonitorResponse).isOnline,
343
+ responseTimeInMs: dnssecResponse?.responseTimeInMs,
344
+ failureCause: dnssecResponse?.failureCause,
345
+ domainName: dnssecResponse?.domainName,
346
+ isZoneSigned: dnssecResponse?.isZoneSigned,
347
+ isParentDsPresent: dnssecResponse?.isParentDsPresent,
348
+ isChainValid: dnssecResponse?.isChainValid,
349
+ resolverConsensusAd: dnssecResponse?.resolverConsensusAd,
350
+ isNameserverConsistent: dnssecResponse?.isNameserverConsistent,
351
+ earliestSignatureExpiration:
352
+ dnssecResponse?.earliestSignatureExpiration,
353
+ daysUntilSignatureExpiry: dnssecResponse?.daysUntilSignatureExpiry,
354
+ dnskeyCount: dnssecResponse?.dnskeys?.length,
355
+ dsRecordCount: dnssecResponse?.parentDsRecords?.length,
356
+ rrsigCount: dnssecResponse?.rrsigs?.length,
357
+ } as JSONObject;
358
+ }
359
+
335
360
  if (
336
361
  data.monitorType === MonitorType.Metrics ||
337
362
  data.monitorType === MonitorType.Kubernetes ||
@@ -84,6 +84,14 @@ export enum CheckOn {
84
84
  DomainStatusCode = "Domain Status Code",
85
85
  DomainIsExpired = "Domain Is Expired",
86
86
 
87
+ // DNSSEC monitors.
88
+ DnssecChainValid = "DNSSEC Chain Is Valid",
89
+ DnssecDnskeyExists = "DNSSEC DNSKEY Record Exists",
90
+ DnssecDsExists = "DNSSEC DS Record Exists At Parent",
91
+ DnssecSignatureExpiresInDays = "DNSSEC Signature Expires In Days",
92
+ DnssecResolverConsensus = "DNSSEC Resolver Consensus (AD Flag)",
93
+ DnssecNameserverConsistent = "DNSSEC Nameservers Are Consistent",
94
+
87
95
  // External Status Page monitors.
88
96
  ExternalStatusPageIsOnline = "External Status Page Is Online",
89
97
  ExternalStatusPageOverallStatus = "External Status Page Overall Status",
@@ -279,6 +287,11 @@ export class CriteriaFilterUtil {
279
287
  checkOn === CheckOn.SnmpIsOnline ||
280
288
  checkOn === CheckOn.DnsIsOnline ||
281
289
  checkOn === CheckOn.DomainIsExpired ||
290
+ checkOn === CheckOn.DnssecChainValid ||
291
+ checkOn === CheckOn.DnssecDnskeyExists ||
292
+ checkOn === CheckOn.DnssecDsExists ||
293
+ checkOn === CheckOn.DnssecResolverConsensus ||
294
+ checkOn === CheckOn.DnssecNameserverConsistent ||
282
295
  checkOn === CheckOn.ExternalStatusPageIsOnline
283
296
  ) {
284
297
  return false;
@@ -0,0 +1,69 @@
1
+ export interface DnssecKeyRecord {
2
+ flags: number;
3
+ algorithm: number;
4
+ keyTag?: number | undefined;
5
+ }
6
+
7
+ export interface DnssecDsRecord {
8
+ keyTag: number;
9
+ algorithm: number;
10
+ digestType: number;
11
+ digest: string;
12
+ }
13
+
14
+ export interface DnssecRrsigRecord {
15
+ typeCovered: string;
16
+ algorithm: number;
17
+ signerName: string;
18
+ keyTag: number;
19
+ inception?: string | undefined;
20
+ expiration?: string | undefined;
21
+ }
22
+
23
+ export interface DnssecResolverCheck {
24
+ resolver: string;
25
+ adFlag: boolean;
26
+ servfailWhenValidating: boolean;
27
+ error?: string | undefined;
28
+ }
29
+
30
+ export interface DnssecNameserverCheck {
31
+ nameServer: string;
32
+ soaSerial?: string | undefined;
33
+ rrsigExpiration?: string | undefined;
34
+ error?: string | undefined;
35
+ }
36
+
37
+ export default interface DnssecMonitorResponse {
38
+ isOnline: boolean;
39
+ responseTimeInMs: number;
40
+ failureCause: string;
41
+ domainName: string;
42
+ isTimeout?: boolean | undefined;
43
+
44
+ // Zone signed?
45
+ isZoneSigned: boolean;
46
+
47
+ // DNSKEY presence
48
+ dnskeys: Array<DnssecKeyRecord>;
49
+
50
+ // DS at parent
51
+ parentDsRecords: Array<DnssecDsRecord>;
52
+ isParentDsPresent: boolean;
53
+
54
+ // RRSIG over the A record (zone apex by default)
55
+ rrsigs: Array<DnssecRrsigRecord>;
56
+ earliestSignatureExpiration?: string | undefined;
57
+ daysUntilSignatureExpiry?: number | undefined;
58
+
59
+ // Resolver consensus (AD flag + CD-bit SERVFAIL test)
60
+ resolverChecks: Array<DnssecResolverCheck>;
61
+ resolverConsensusAd: boolean;
62
+
63
+ // Primary/secondary nameserver consistency
64
+ nameserverChecks: Array<DnssecNameserverCheck>;
65
+ isNameserverConsistent: boolean;
66
+
67
+ // Overall chain validity (DNSKEY exists, DS exists, RRSIG valid, AD across resolvers)
68
+ isChainValid: boolean;
69
+ }
@@ -487,6 +487,33 @@ export default class MonitorCriteriaInstance extends DatabaseProperty {
487
487
  return monitorCriteriaInstance;
488
488
  }
489
489
 
490
+ if (arg.monitorType === MonitorType.DNSSEC) {
491
+ const monitorCriteriaInstance: MonitorCriteriaInstance =
492
+ new MonitorCriteriaInstance();
493
+
494
+ monitorCriteriaInstance.data = {
495
+ id: ObjectID.generate().toString(),
496
+ monitorStatusId: arg.monitorStatusId,
497
+ filterCondition: FilterCondition.All,
498
+ filters: [
499
+ {
500
+ checkOn: CheckOn.DnssecChainValid,
501
+ filterType: FilterType.True,
502
+ value: undefined,
503
+ },
504
+ ],
505
+ incidents: [],
506
+ alerts: [],
507
+ createAlerts: false,
508
+ changeMonitorStatus: true,
509
+ createIncidents: false,
510
+ name: `Check if ${arg.monitorName} DNSSEC chain is valid`,
511
+ description: `This criteria checks if the ${arg.monitorName} DNSSEC chain is valid end-to-end`,
512
+ };
513
+
514
+ return monitorCriteriaInstance;
515
+ }
516
+
490
517
  if (arg.monitorType === MonitorType.ExternalStatusPage) {
491
518
  const monitorCriteriaInstance: MonitorCriteriaInstance =
492
519
  new MonitorCriteriaInstance();
@@ -695,6 +722,46 @@ export default class MonitorCriteriaInstance extends DatabaseProperty {
695
722
  };
696
723
  }
697
724
 
725
+ if (arg.monitorType === MonitorType.DNSSEC) {
726
+ monitorCriteriaInstance.data = {
727
+ id: ObjectID.generate().toString(),
728
+ monitorStatusId: arg.monitorStatusId,
729
+ filterCondition: FilterCondition.Any,
730
+ filters: [
731
+ {
732
+ checkOn: CheckOn.DnssecChainValid,
733
+ filterType: FilterType.False,
734
+ value: undefined,
735
+ },
736
+ ],
737
+ incidents: [
738
+ {
739
+ title: `${arg.monitorName} DNSSEC chain is broken`,
740
+ description: `${arg.monitorName} DNSSEC validation is currently failing.`,
741
+ incidentSeverityId: arg.incidentSeverityId,
742
+ autoResolveIncident: true,
743
+ id: ObjectID.generate().toString(),
744
+ onCallPolicyIds: [],
745
+ },
746
+ ],
747
+ changeMonitorStatus: true,
748
+ createIncidents: true,
749
+ createAlerts: false,
750
+ alerts: [
751
+ {
752
+ title: `${arg.monitorName} DNSSEC chain is broken`,
753
+ description: `${arg.monitorName} DNSSEC validation is currently failing.`,
754
+ alertSeverityId: arg.alertSeverityId,
755
+ autoResolveAlert: true,
756
+ id: ObjectID.generate().toString(),
757
+ onCallPolicyIds: [],
758
+ },
759
+ ],
760
+ name: `Check if ${arg.monitorName} DNSSEC chain is broken`,
761
+ description: `This criteria checks if the ${arg.monitorName} DNSSEC chain is broken`,
762
+ };
763
+ }
764
+
698
765
  if (arg.monitorType === MonitorType.ExternalStatusPage) {
699
766
  monitorCriteriaInstance.data = {
700
767
  id: ObjectID.generate().toString(),
@@ -38,6 +38,9 @@ import MonitorStepDnsMonitor, {
38
38
  import MonitorStepDomainMonitor, {
39
39
  MonitorStepDomainMonitorUtil,
40
40
  } from "./MonitorStepDomainMonitor";
41
+ import MonitorStepDnssecMonitor, {
42
+ MonitorStepDnssecMonitorUtil,
43
+ } from "./MonitorStepDnssecMonitor";
41
44
  import MonitorStepExternalStatusPageMonitor, {
42
45
  MonitorStepExternalStatusPageMonitorUtil,
43
46
  } from "./MonitorStepExternalStatusPageMonitor";
@@ -109,6 +112,9 @@ export interface MonitorStepType {
109
112
  // Domain monitor
110
113
  domainMonitor?: MonitorStepDomainMonitor | undefined;
111
114
 
115
+ // DNSSEC monitor
116
+ dnssecMonitor?: MonitorStepDnssecMonitor | undefined;
117
+
112
118
  // External Status Page monitor
113
119
  externalStatusPageMonitor?: MonitorStepExternalStatusPageMonitor | undefined;
114
120
 
@@ -150,6 +156,7 @@ export default class MonitorStep extends DatabaseProperty {
150
156
  snmpMonitor: undefined,
151
157
  dnsMonitor: undefined,
152
158
  domainMonitor: undefined,
159
+ dnssecMonitor: undefined,
153
160
  externalStatusPageMonitor: undefined,
154
161
  kubernetesMonitor: undefined,
155
162
  dockerMonitor: undefined,
@@ -191,6 +198,7 @@ export default class MonitorStep extends DatabaseProperty {
191
198
  snmpMonitor: undefined,
192
199
  dnsMonitor: undefined,
193
200
  domainMonitor: undefined,
201
+ dnssecMonitor: undefined,
194
202
  externalStatusPageMonitor: undefined,
195
203
  kubernetesMonitor: undefined,
196
204
  dockerMonitor: undefined,
@@ -334,6 +342,13 @@ export default class MonitorStep extends DatabaseProperty {
334
342
  return this;
335
343
  }
336
344
 
345
+ public setDnssecMonitor(
346
+ dnssecMonitor: MonitorStepDnssecMonitor,
347
+ ): MonitorStep {
348
+ this.data!.dnssecMonitor = dnssecMonitor;
349
+ return this;
350
+ }
351
+
337
352
  public setExternalStatusPageMonitor(
338
353
  externalStatusPageMonitor: MonitorStepExternalStatusPageMonitor,
339
354
  ): MonitorStep {
@@ -508,6 +523,23 @@ export default class MonitorStep extends DatabaseProperty {
508
523
  }
509
524
  }
510
525
 
526
+ if (monitorType === MonitorType.DNSSEC) {
527
+ if (!value.data.dnssecMonitor) {
528
+ return "DNSSEC configuration is required";
529
+ }
530
+
531
+ if (!value.data.dnssecMonitor.domainName) {
532
+ return "Domain name is required";
533
+ }
534
+
535
+ if (
536
+ !value.data.dnssecMonitor.resolvers ||
537
+ value.data.dnssecMonitor.resolvers.length === 0
538
+ ) {
539
+ return "At least one resolver is required";
540
+ }
541
+ }
542
+
511
543
  if (monitorType === MonitorType.ExternalStatusPage) {
512
544
  if (!value.data.externalStatusPageMonitor) {
513
545
  return "External status page configuration is required";
@@ -600,6 +632,9 @@ export default class MonitorStep extends DatabaseProperty {
600
632
  domainMonitor: this.data.domainMonitor
601
633
  ? MonitorStepDomainMonitorUtil.toJSON(this.data.domainMonitor)
602
634
  : undefined,
635
+ dnssecMonitor: this.data.dnssecMonitor
636
+ ? MonitorStepDnssecMonitorUtil.toJSON(this.data.dnssecMonitor)
637
+ : undefined,
603
638
  externalStatusPageMonitor: this.data.externalStatusPageMonitor
604
639
  ? MonitorStepExternalStatusPageMonitorUtil.toJSON(
605
640
  this.data.externalStatusPageMonitor,
@@ -734,6 +769,9 @@ export default class MonitorStep extends DatabaseProperty {
734
769
  domainMonitor: json["domainMonitor"]
735
770
  ? (json["domainMonitor"] as JSONObject)
736
771
  : undefined,
772
+ dnssecMonitor: json["dnssecMonitor"]
773
+ ? (json["dnssecMonitor"] as JSONObject)
774
+ : undefined,
737
775
  externalStatusPageMonitor: json["externalStatusPageMonitor"]
738
776
  ? (json["externalStatusPageMonitor"] as JSONObject)
739
777
  : undefined,
@@ -775,6 +813,7 @@ export default class MonitorStep extends DatabaseProperty {
775
813
  snmpMonitor: Zod.any().optional(),
776
814
  dnsMonitor: Zod.any().optional(),
777
815
  domainMonitor: Zod.any().optional(),
816
+ dnssecMonitor: Zod.any().optional(),
778
817
  externalStatusPageMonitor: Zod.any().optional(),
779
818
  kubernetesMonitor: Zod.any().optional(),
780
819
  dockerMonitor: Zod.any().optional(),
@@ -0,0 +1,59 @@
1
+ import { JSONObject } from "../JSON";
2
+
3
+ export default interface MonitorStepDnssecMonitor {
4
+ domainName: string;
5
+ resolvers: Array<string>;
6
+ checkNameserverConsistency: boolean;
7
+ signatureExpiryWarningDays: number;
8
+ timeout: number;
9
+ retries: number;
10
+ }
11
+
12
+ export class MonitorStepDnssecMonitorUtil {
13
+ public static getDefault(): MonitorStepDnssecMonitor {
14
+ return {
15
+ domainName: "",
16
+ resolvers: ["1.1.1.1", "8.8.8.8", "9.9.9.9"],
17
+ checkNameserverConsistency: true,
18
+ signatureExpiryWarningDays: 7,
19
+ timeout: 10000,
20
+ retries: 3,
21
+ };
22
+ }
23
+
24
+ public static fromJSON(json: JSONObject): MonitorStepDnssecMonitor {
25
+ const defaults: MonitorStepDnssecMonitor =
26
+ MonitorStepDnssecMonitorUtil.getDefault();
27
+
28
+ const resolvers: Array<string> = Array.isArray(json["resolvers"])
29
+ ? (json["resolvers"] as Array<string>).filter((value: unknown) => {
30
+ return typeof value === "string" && value.length > 0;
31
+ })
32
+ : defaults.resolvers;
33
+
34
+ return {
35
+ domainName: (json["domainName"] as string) || "",
36
+ resolvers: resolvers.length > 0 ? resolvers : defaults.resolvers,
37
+ checkNameserverConsistency:
38
+ typeof json["checkNameserverConsistency"] === "boolean"
39
+ ? (json["checkNameserverConsistency"] as boolean)
40
+ : defaults.checkNameserverConsistency,
41
+ signatureExpiryWarningDays:
42
+ (json["signatureExpiryWarningDays"] as number) ||
43
+ defaults.signatureExpiryWarningDays,
44
+ timeout: (json["timeout"] as number) || defaults.timeout,
45
+ retries: (json["retries"] as number) || defaults.retries,
46
+ };
47
+ }
48
+
49
+ public static toJSON(monitor: MonitorStepDnssecMonitor): JSONObject {
50
+ return {
51
+ domainName: monitor.domainName,
52
+ resolvers: monitor.resolvers,
53
+ checkNameserverConsistency: monitor.checkNameserverConsistency,
54
+ signatureExpiryWarningDays: monitor.signatureExpiryWarningDays,
55
+ timeout: monitor.timeout,
56
+ retries: monitor.retries,
57
+ };
58
+ }
59
+ }
@@ -32,6 +32,9 @@ enum MonitorType {
32
32
  // DNS monitoring
33
33
  DNS = "DNS",
34
34
 
35
+ // DNSSEC validation monitoring
36
+ DNSSEC = "DNSSEC",
37
+
35
38
  // Domain registration monitoring
36
39
  Domain = "Domain",
37
40
 
@@ -64,12 +67,15 @@ export class MonitorTypeHelper {
64
67
  MonitorType.Ping,
65
68
  MonitorType.IP,
66
69
  MonitorType.Port,
67
- MonitorType.DNS,
68
70
  MonitorType.SSLCertificate,
69
71
  MonitorType.Domain,
70
72
  MonitorType.ExternalStatusPage,
71
73
  ],
72
74
  },
75
+ {
76
+ label: "DNS Monitoring",
77
+ monitorTypes: [MonitorType.DNS, MonitorType.DNSSEC],
78
+ },
73
79
  {
74
80
  label: "Synthetic Monitoring",
75
81
  monitorTypes: [
@@ -273,6 +279,13 @@ export class MonitorTypeHelper {
273
279
  "This monitor type lets you monitor DNS resolution for your domains, verify record values, and check DNSSEC validity.",
274
280
  icon: IconProp.GlobeAlt,
275
281
  },
282
+ {
283
+ monitorType: MonitorType.DNSSEC,
284
+ title: "DNSSEC",
285
+ description:
286
+ "This monitor type performs full DNSSEC validation — DNSKEY, DS at the parent zone, RRSIG validity windows, AD-flag/SERVFAIL behavior across public resolvers, and primary/secondary nameserver consistency.",
287
+ icon: IconProp.Key,
288
+ },
276
289
  {
277
290
  monitorType: MonitorType.Domain,
278
291
  title: "Domain",
@@ -334,6 +347,7 @@ export class MonitorTypeHelper {
334
347
  monitorType === MonitorType.CustomJavaScriptCode ||
335
348
  monitorType === MonitorType.SNMP ||
336
349
  monitorType === MonitorType.DNS ||
350
+ monitorType === MonitorType.DNSSEC ||
337
351
  monitorType === MonitorType.Domain ||
338
352
  monitorType === MonitorType.ExternalStatusPage;
339
353
  return isProbeableMonitor;
@@ -359,6 +373,7 @@ export class MonitorTypeHelper {
359
373
  MonitorType.Profiles,
360
374
  MonitorType.SNMP,
361
375
  MonitorType.DNS,
376
+ MonitorType.DNSSEC,
362
377
  MonitorType.Domain,
363
378
  MonitorType.ExternalStatusPage,
364
379
  MonitorType.Kubernetes,
@@ -397,6 +412,7 @@ export class MonitorTypeHelper {
397
412
  monitorType === MonitorType.CustomJavaScriptCode ||
398
413
  monitorType === MonitorType.SNMP ||
399
414
  monitorType === MonitorType.DNS ||
415
+ monitorType === MonitorType.DNSSEC ||
400
416
  monitorType === MonitorType.Domain ||
401
417
  monitorType === MonitorType.ExternalStatusPage
402
418
  ) {
@@ -9,6 +9,7 @@ import SyntheticMonitorResponse from "../Monitor/SyntheticMonitors/SyntheticMoni
9
9
  import SnmpMonitorResponse from "../Monitor/SnmpMonitor/SnmpMonitorResponse";
10
10
  import DnsMonitorResponse from "../Monitor/DnsMonitor/DnsMonitorResponse";
11
11
  import DomainMonitorResponse from "../Monitor/DomainMonitor/DomainMonitorResponse";
12
+ import DnssecMonitorResponse from "../Monitor/DnssecMonitor/DnssecMonitorResponse";
12
13
  import ExternalStatusPageMonitorResponse from "../Monitor/ExternalStatusPageMonitor/ExternalStatusPageMonitorResponse";
13
14
  import MonitorEvaluationSummary from "../Monitor/MonitorEvaluationSummary";
14
15
  import ObjectID from "../ObjectID";
@@ -35,6 +36,7 @@ export default interface ProbeMonitorResponse {
35
36
  snmpResponse?: SnmpMonitorResponse | undefined;
36
37
  dnsResponse?: DnsMonitorResponse | undefined;
37
38
  domainResponse?: DomainMonitorResponse | undefined;
39
+ dnssecResponse?: DnssecMonitorResponse | undefined;
38
40
  externalStatusPageResponse?: ExternalStatusPageMonitorResponse | undefined;
39
41
  monitoredAt: Date;
40
42
  isTimeout?: boolean | undefined;
@@ -405,6 +405,57 @@ export default class TemplateVariablesCatalog {
405
405
  ],
406
406
  };
407
407
 
408
+ case MonitorType.DNSSEC:
409
+ return {
410
+ title: "DNSSEC",
411
+ variables: [
412
+ {
413
+ key: "isOnline",
414
+ description: "True if the DNSSEC check completed.",
415
+ },
416
+ { key: "domainName", description: "Zone queried." },
417
+ {
418
+ key: "isZoneSigned",
419
+ description: "True if any DNSKEY records exist for the zone.",
420
+ },
421
+ {
422
+ key: "isParentDsPresent",
423
+ description: "True if DS records exist at the parent zone.",
424
+ },
425
+ {
426
+ key: "isChainValid",
427
+ description:
428
+ "True if DNSKEY, DS, RRSIG, and resolver AD-flag all check out.",
429
+ },
430
+ {
431
+ key: "resolverConsensusAd",
432
+ description:
433
+ "True if every configured resolver returned the AD flag.",
434
+ },
435
+ {
436
+ key: "isNameserverConsistent",
437
+ description:
438
+ "True if every authoritative nameserver returned the same SOA serial.",
439
+ },
440
+ {
441
+ key: "earliestSignatureExpiration",
442
+ description: "Earliest RRSIG expiration timestamp (ISO).",
443
+ },
444
+ {
445
+ key: "daysUntilSignatureExpiry",
446
+ description: "Days until the earliest RRSIG expires.",
447
+ },
448
+ { key: "dnskeyCount", description: "Number of DNSKEY records." },
449
+ {
450
+ key: "dsRecordCount",
451
+ description: "Number of DS records at the parent.",
452
+ },
453
+ { key: "rrsigCount", description: "Number of RRSIG records seen." },
454
+ { key: "responseTimeInMs", description: "Total check duration." },
455
+ { key: "failureCause", description: "Failure reason." },
456
+ ],
457
+ };
458
+
408
459
  case MonitorType.ExternalStatusPage:
409
460
  return {
410
461
  title: "External Status Page",
@@ -171,6 +171,7 @@ class MonitorMetricTypeUtil {
171
171
  monitorType === MonitorType.Port ||
172
172
  monitorType === MonitorType.SNMP ||
173
173
  monitorType === MonitorType.DNS ||
174
+ monitorType === MonitorType.DNSSEC ||
174
175
  monitorType === MonitorType.Domain ||
175
176
  monitorType === MonitorType.ExternalStatusPage
176
177
  ) {