# Blackbox DNS probe definitions, shared between the exporter module # (modules/monitoring/blackbox.nix, which renders these into the blackbox # config) and the scraper (modules/monitoring/server.nix, which turns them into # VictoriaMetrics scrape jobs). Kept in one place so the module list and the # scrape jobs can never drift apart. # # These query the nameservers' PUBLIC addresses, i.e. the path a real internet # resolver takes, not the mesh — the whole point is to catch outside-in # resolution failures the Knot stats can't see. For each zone we run two probes # per endpoint: an SOA query (is the zone being served at all?) and a DNSKEY # query (is it still DNSSEC-signed?). Blackbox has no DO-bit option, so we ask # for DNSKEY directly — an authoritative signed zone returns it without EDNS0, # and its absence means signing has broken. { lib }: let domains = import ../dns/domains.nix; blackboxAddr = "127.0.0.1:9115"; # Public endpoints of the authoritative nameservers. The v4 addresses also # appear in the `internet` instance in clan.nix; the v6 ones in each ns # machine's cnx.staticIPv6. IPv6 literals are bracketed for host:port. endpoints = [ { instance = "ns1 v4"; target = "46.224.170.206:53"; } { instance = "ns1 v6"; target = "[2a01:4f8:c014:b5c5::1]:53"; } { instance = "ns2 v4"; target = "157.180.70.82:53"; } { instance = "ns2 v6"; target = "[2a01:4f9:c014:6d87::1]:53"; } ]; queries = [ { name = "soa"; type = "SOA"; } { name = "dnskey"; type = "DNSKEY"; } ]; sanitize = lib.replaceStrings [ "." ] [ "_" ]; moduleName = zone: q: "dns_${q.name}_${sanitize zone}"; modules = lib.listToAttrs ( lib.concatMap ( zone: map ( q: lib.nameValuePair (moduleName zone q) { prober = "dns"; timeout = "5s"; dns = { query_name = "${zone}."; query_type = q.type; valid_rcodes = [ "NOERROR" ]; # Fail unless at least one answer RR of the queried type is present: # a NOERROR with an empty answer (or a missing DNSKEY) still fails. validate_answer_rrs.fail_if_not_matches_regexp = [ "\\s${q.type}\\s" ]; }; } ) queries ) domains ); scrapeConfigs = lib.concatMap ( zone: map (q: { job_name = "blackbox_${moduleName zone q}"; metrics_path = "/probe"; params.module = [ (moduleName zone q) ]; static_configs = map (e: { targets = [ e.target ]; labels = { instance = e.instance; zone = zone; query = q.type; }; }) endpoints; # Hand the real DNS server to blackbox as ?target=, then point the scrape # at the exporter itself. relabel_configs = [ { source_labels = [ "__address__" ]; target_label = "__param_target"; } { target_label = "__address__"; replacement = blackboxAddr; } ]; }) queries ) domains; in { inherit modules scrapeConfigs blackboxAddr; }