Nix-dns: A Nix DSL for DNS zone files

Writing Nix DSLs for DNS seems to be a popular topic, so to throw a thirdsecond (the pull request on tinydns isn’t a full DSL but only a set of helpers for generating strings for a few resource records) implementation into the mix:

Example:

While both implementations seem to be somewhat similar, there are a few differences:

  • Nix-dns uses an explicit subdomains option to distinguish between resource records and subdomains, whereas my implementation involves a bit more magic in that every non-uppercase name or defaultTTL to distinguish between them. While this involves more magic, it also leads to a more concise specification, for example com.example.www instead of com.subdomains.example.subdomains.www.
  • My implementation is more tailored towards the NixOS module system and basically builds a global DNS tree for all domains with only an isZone flag to specify whether the domain is a zone (the flag is true by default if a SOA record is defined). The reason for this is to be able to specify resource records across several modules.
  • While the nix-dns implementation is also using the NixOS module system it is less tied towards the module system and could more easily be used outside of it.
  • My implementation has specific types for relative/absolute domains, IPv4 and IPv6 addresses and thus can eg. check for typos in labels across domains. This also means that the implementation isn’t as lightweight as nix-dns.
  • Nix-dns also has a small set of combinators and has more predefined record types (wrong, both have the same set of predefined records plus my implementation has SSHFP).
  • Another major difference is how the records are structured internally: While nix-dns generates record-specific options, my implementation uses the structured record options to generate a generic resource record definition that is used for all resource records. The rationale behind the latter is that having a such a generic option it would be rather easy to write generators for specific zone file formats (currently it only generates bind zone files).

However, please take it with a grain of salt, since I’m obviously biased towards my own implementation.

While it would be great to have such an abstraction upstream, I’ve so far been reluctant to upstream it, since it does come with additional complexity and of course also some magic (see above).

The reason however why I made it the way it turned out to be is that this design makes it easy to define records on a per-module and thus per-service basis and also make it discoverable via the nodes attribute in my deployments, eg. to use something like nodes.ns1.config.dns.zones.com.example.A.value in other services.

So personally, I’d choose option 1 and just specify the the roots which a specific DNS server should serve, that way you also don’t need to deal with different zone files and have it discoverable.

3 Likes