keepcalculator

06 Numbers and Powers

Detect Tenant to DNS Drift in Node.js

Card No. 06·63682 · Updated · Standard formula, shown below

This guide shows you how to detect when a tenant’s expected DNS configuration drifts from what’s actually resolved in Node.js. You’ll write a script that compares a tenant’s desired DNS records (like A, AAAA, CNAME, MX) against live DNS lookups, and reports mismatches. You’ll need Node.js 18+ and the `dns` module (built-in) or the `dns-promises` API.

What You’ll Need

  • Node.js 18 or newer (for built-in fetch and promises)
  • A list of tenant IDs and their expected DNS records (e.g., from a database or config file)
  • Access to the DNS servers you want to query (default system resolvers are fine)
  • Basic familiarity with async/await and error handling

Step-by-Step

  1. Set up your project. Create a new directory and run npm init -y. Install no external packages—Node’s built-in dns/promises is enough.
  2. Define the expected DNS configuration. Create a JSON file or an array in your script that maps tenant IDs to their expected records. Example:
    {
      "tenant1": {
        "A": ["192.0.2.1"],
        "CNAME": ["app.example.com"]
      },
      "tenant2": {
        "MX": ["mail.tenant2.com"]
      }
    }
  3. Import the DNS promises module. At the top of your script: const dns = require('dns').promises; or import { promises as dns } from 'dns'; if using ES modules.
  4. Write a function to resolve a given record type. Use dns.resolve(hostname, rrtype). For A records it returns an array of IPs; for CNAME it returns a single string; for MX it returns an array of objects with priority and exchange. Wrap it in try/catch to handle NXDOMAIN or other DNS errors.
  5. Normalize the resolved data. Convert the resolved records into a comparable format—sort arrays, lowercase hostnames, and for MX sort by priority. This avoids false positives due to ordering.
  6. Compare expected vs actual. Write a function that takes the expected record and the actual record, and returns true if they match (deep equality after normalization), false otherwise. For MX, compare the sorted list of exchange values (and optionally priorities).
  7. Loop through all tenants. For each tenant, for each record type defined in the expected config, resolve the tenant’s hostname (e.g., tenant1.example.com) and compare. Collect mismatches into an array.
  8. Handle missing records. If a DNS lookup throws (e.g., ENOTFOUND), treat it as a mismatch: expected records exist but none are found.
  9. Output the results. Log each mismatch with tenant ID, record type, expected value, and actual value. If no mismatches, log “All tenants in sync”.
  10. Add a CLI wrapper. Use process.argv to accept a path to the config file, or just hardcode for a quick test. Optionally add a --json flag for machine-readable output.
  11. Run the script. Execute with node check-dns.js. Ensure your system can resolve the hostnames—test with nslookup first if unsure.

Common Mistakes

  • Not handling DNS errors properly. A failed lookup is not the same as “no records”. Use try/catch and treat errors as mismatches unless the error is clearly a timeout (then retry).
  • Ignoring record ordering. DNS responses are not guaranteed to be in the same order every time. Always sort arrays before comparing.
  • Forgetting TTL. DNS results are cached. If you run the check too frequently, you might see stale data. Consider using resolve4 with { ttl: true } to get TTL, or just accept some delay.
  • Assuming CNAME is an array. dns.resolve returns a string for CNAME, not an array. Normalize it to an array for uniform comparison.
  • Not lowercasing hostnames. DNS is case-insensitive, but your expected config might have uppercase. Normalize to lowercase.

Tips

  • Use a custom DNS server. For testing, you can pass a server to dns.setServers() to point to a test DNS server.
  • Add concurrency limits. If you have many tenants, use Promise.all with a concurrency limit (e.g., via a simple counter) to avoid overwhelming the DNS server.
  • Log with timestamps. Include new Date().toISOString() in each mismatch log for auditing.
  • Write a test config. Create a dummy tenant with known-good and known-bad records to verify your script works.
  • Consider using a library like dns2 if you need more control over the wire protocol. For most cases, built-in is fine.

Troubleshooting

  • Script throws ENOTFOUND for every tenant. Check your network connection and DNS server settings. Try nslookup example.com to verify resolution works.
  • Results are always mismatched even when they shouldn’t be. Check your normalization—maybe you’re comparing arrays with different order. Log the actual and expected values to inspect.
  • MX records show as mismatched due to priority. If you don’t care about priority, only compare the exchange hostnames. If you do, ensure you sort by priority.
  • Script hangs. DNS timeouts can be long. Set a timeout using AbortController or dns.resolve with a timeout option (not directly supported, so use a wrapper with setTimeout).
  • CNAME records appear as an array with one element. That’s fine—just normalize to a string or compare the first element.

Frequently Asked Questions

What types of DNS records can I check?

Any type supported by dns.resolve: A, AAAA, CNAME, MX, NS, TXT, SRV, PTR, SOA. The comparison logic may need adjustment per type (e.g., TXT is an array of strings).

How do I handle a tenant that has no DNS records at all?

If the expected config has no records for a tenant, you can skip it or check that the hostname returns NXDOMAIN. If you expect records but get NXDOMAIN, that’s a drift.

Can I run this on a schedule?

Yes, wrap the script in a cron job or use setInterval in a long-running process. Be mindful of rate limits on your DNS server.

What if I need to check DNS from a different resolver?

Use dns.setServers(['8.8.8.8']) before resolving. This changes the resolver for all subsequent queries.

How do I compare TXT records?

TXT records are arrays of strings. Sort them and compare as arrays. Be aware that TXT records can be split into multiple strings; you may need to join them.

Is this method reliable for production?

It’s a good baseline, but for production consider using a dedicated DNS monitoring service. Also, account for caching and propagation delays (TTL).

Related

Method: the standard formula for this calculation, rounded as each result states. See how the calculators work or report an error.

More cards in this drawer

The whole Numbers and Powers drawer →