feat: Implement DNS monitoring configuration and secret handling

This commit is contained in:
Nawaz Dhandala
2026-02-13 13:42:45 +00:00
parent f6a8cef649
commit f324a4e864
5 changed files with 212 additions and 1 deletions

View File

@@ -521,6 +521,19 @@ const dataTypeDetails: Dictionary<DataTypePageData> = {
},
],
},
{
name: "dnsMonitor",
type: "MonitorStepDnsMonitor",
required: false,
description:
"Configuration for DNS monitoring. Required for DNS monitor type. Defines query name (domain), record type, optional DNS server, port, timeout, and retry settings. See MonitorStepDnsMonitor.",
typeLinks: [
{
label: "MonitorStepDnsMonitor",
path: "monitor-step-dns-monitor",
},
],
},
],
values: [],
jsonExample: JSON.stringify(
@@ -2560,6 +2573,31 @@ const dataTypeDetails: Dictionary<DataTypePageData> = {
description:
"Whether the SNMP device is reachable. Use with 'True' or 'False'. Applies to: SNMP monitors.",
},
{
value: "DNS Response Time (in ms)",
description:
"The DNS query response time in milliseconds. Use with numeric FilterTypes. Applies to: DNS monitors.",
},
{
value: "DNS Is Online",
description:
"Whether the DNS resolution succeeded. Use with 'True' or 'False'. Applies to: DNS monitors.",
},
{
value: "DNS Record Value",
description:
"The value of a DNS record returned by the query. Use with string FilterTypes (Contains, EqualTo, etc.). Applies to: DNS monitors.",
},
{
value: "DNSSEC Is Valid",
description:
"Whether DNSSEC validation passed (AD flag present). Use with 'True' or 'False'. Applies to: DNS monitors.",
},
{
value: "DNS Record Exists",
description:
"Whether any DNS records were returned for the query. Use with 'True' or 'False'. Applies to: DNS monitors.",
},
{
value: "JavaScript Expression",
description:
@@ -3112,6 +3150,94 @@ const dataTypeDetails: Dictionary<DataTypePageData> = {
2,
),
},
"monitor-step-dns-monitor": {
title: "MonitorStepDnsMonitor",
description:
"Configuration for a DNS monitor step. Defines the domain to query, record type, optional custom DNS server, and timeout settings. Used as the 'dnsMonitor' property on a MonitorStep when the monitor type is 'DNS'. The criteria filters can then use 'DNS Is Online', 'DNS Response Time (in ms)', 'DNS Record Value', 'DNSSEC Is Valid', and 'DNS Record Exists' as CheckOn values.",
isEnum: false,
relatedTypes: [
{
name: "MonitorStep",
path: "monitor-step",
relationship: "Parent that holds this as dnsMonitor property",
},
{
name: "CheckOn",
path: "check-on",
relationship: "Use DNS-specific CheckOn values with DNS monitors",
},
],
properties: [
{
name: "queryName",
type: "string",
required: true,
description:
"The domain name to query (e.g., 'example.com').",
},
{
name: "recordType",
type: "string (enum)",
required: true,
description:
"The DNS record type to query. Possible values: 'A', 'AAAA', 'CNAME', 'MX', 'NS', 'TXT', 'SOA', 'PTR', 'SRV', 'CAA'.",
},
{
name: "hostname",
type: "string",
required: false,
description:
"Custom DNS server to use for the query (e.g., '8.8.8.8'). Leave empty to use system default DNS resolver.",
},
{
name: "port",
type: "number",
required: false,
description: "DNS port. Default is 53.",
},
{
name: "timeout",
type: "number",
required: false,
description:
"Timeout for DNS queries in milliseconds. Default is 5000 (5 seconds).",
},
{
name: "retries",
type: "number",
required: false,
description: "Number of retries for failed DNS queries. Default is 3.",
},
],
values: [],
jsonExample: JSON.stringify(
{
"// Example 1: Basic A record lookup": {
queryName: "example.com",
recordType: "A",
timeout: 5000,
retries: 3,
},
"// Example 2: MX record with custom DNS server": {
queryName: "example.com",
recordType: "MX",
hostname: "8.8.8.8",
port: 53,
timeout: 5000,
retries: 3,
},
"// Example 3: TXT record for SPF verification": {
queryName: "example.com",
recordType: "TXT",
hostname: "1.1.1.1",
timeout: 5000,
retries: 3,
},
},
null,
2,
),
},
};
export default class ServiceHandler {

View File

@@ -127,6 +127,14 @@ export class Service extends DatabaseService<Model> {
monitorDestination = `${monitorDestination}:${port}`;
}
}
// For DNS monitors, use the queryName from dnsMonitor config
if (monitorType === MonitorType.DNS && firstStep?.data?.dnsMonitor) {
monitorDestination = firstStep.data.dnsMonitor.queryName || "";
if (firstStep.data.dnsMonitor.hostname) {
monitorDestination = `${monitorDestination} @${firstStep.data.dnsMonitor.hostname}`;
}
}
}
}

View File

@@ -14,6 +14,9 @@ import SyntheticMonitorResponse from "../../../Types/Monitor/SyntheticMonitors/S
import SnmpMonitorResponse, {
SnmpOidResponse,
} from "../../../Types/Monitor/SnmpMonitor/SnmpMonitorResponse";
import DnsMonitorResponse, {
DnsRecordResponse,
} from "../../../Types/Monitor/DnsMonitor/DnsMonitorResponse";
import Typeof from "../../../Types/Typeof";
import VMUtil from "../VM/VMAPI";
import DataToProcess from "./DataToProcess";
@@ -240,6 +243,40 @@ export default class MonitorTemplateUtil {
}
}
}
if (data.monitorType === MonitorType.DNS) {
const dnsResponse: DnsMonitorResponse | undefined = (
data.dataToProcess as ProbeMonitorResponse
).dnsResponse;
storageMap = {
isOnline: (data.dataToProcess as ProbeMonitorResponse).isOnline,
responseTimeInMs: dnsResponse?.responseTimeInMs,
failureCause: dnsResponse?.failureCause,
isTimeout: dnsResponse?.isTimeout,
isDnssecValid: dnsResponse?.isDnssecValid,
} as JSONObject;
// Add DNS records
if (dnsResponse?.records) {
storageMap["records"] = dnsResponse.records.map(
(record: DnsRecordResponse) => {
return {
type: record.type,
value: record.value,
ttl: record.ttl,
};
},
);
// Add record values as a flat array for easier templating
storageMap["recordValues"] = dnsResponse.records.map(
(record: DnsRecordResponse) => {
return record.value;
},
);
}
}
} catch (err) {
logger.error(err);
}

View File

@@ -85,7 +85,8 @@ class MonitorMetricTypeUtil {
monitorType === MonitorType.Ping ||
monitorType === MonitorType.IP ||
monitorType === MonitorType.Port ||
monitorType === MonitorType.SNMP
monitorType === MonitorType.SNMP ||
monitorType === MonitorType.DNS
) {
return [MonitorMetricType.IsOnline, MonitorMetricType.ResponseTime];
}

View File

@@ -196,6 +196,45 @@ export default class MonitorUtil {
}
}
if (monitorType === MonitorType.DNS) {
for (const monitorStep of monitorSteps?.data?.monitorStepsInstanceArray ||
[]) {
// Handle DNS hostname secrets (custom DNS server)
if (
monitorStep.data?.dnsMonitor?.hostname &&
this.hasSecrets(monitorStep.data.dnsMonitor.hostname)
) {
if (!isSecretsLoaded) {
monitorSecrets = await MonitorUtil.loadMonitorSecrets(monitorId);
isSecretsLoaded = true;
}
monitorStep.data.dnsMonitor.hostname =
(await MonitorUtil.fillSecretsInStringOrJSON({
secrets: monitorSecrets,
populateSecretsIn: monitorStep.data.dnsMonitor.hostname,
})) as string;
}
// Handle DNS query name secrets
if (
monitorStep.data?.dnsMonitor?.queryName &&
this.hasSecrets(monitorStep.data.dnsMonitor.queryName)
) {
if (!isSecretsLoaded) {
monitorSecrets = await MonitorUtil.loadMonitorSecrets(monitorId);
isSecretsLoaded = true;
}
monitorStep.data.dnsMonitor.queryName =
(await MonitorUtil.fillSecretsInStringOrJSON({
secrets: monitorSecrets,
populateSecretsIn: monitorStep.data.dnsMonitor.queryName,
})) as string;
}
}
}
return monitorSteps;
}