TLDR: zeronsd is a new, standalone unicast DNS server that integrates with ZeroTier Central. You can find it here. It is currently in alpha testing; please read on if this is interesting!
Some of you know that ZeroTier has a good layer 2 story already, allowing for things like Multicast DNS to function transparently on your network without any changes. For those of you who don’t want to click through, this is what resolves networks on your Macs and Windows operating systems to DNS names on the
.local network. Linux/BSD users can use Avahi to achieve the same effect.
There are a lot of things that Multicast DNS doesn’t do well: reliability and the notion of the record only existing when the machine is online are two easily-found fruits we can pick up off the floor, as they are an immediate, present problem for any production environment. If your DNS record no longer exists because the machine rebooted/is off/caught fire, the resolving host is going to respond to it very differently (and much slower) than if the record exists and the machine is offline. Likewise, if the link is slow or unresponsive (as can be the case with hundreds of computers behaving like a LAN across the globe), the DNS record will cease to exist, temporarily. That’s bad, yo.
So, while layer 2 LLMNR and mDNS definitely fill a void in your use of ZeroTier, they do not solve a lot of problems with larger networks, leaving people to resort to those pesky multi-octet beasts we call IP addresses.
A while back, I discovered the trust-dns toolkit in Rust; after having made my own DNS service in Golang for my home network, I found the trust-dns API to be quite refreshing and easy to use, as well as quite performant; there are numbers late in the article.
zeronsd provides unicast resolution for ZeroTier networks
zeronsd was the product of that experimentation, and provides a number of features you may appreciate as a ZeroTier user:
- • One network, one service. No magic multi-network beast to manage.
- • Easy command-line usage.
- • Auto-detection of listening IP.
- • Auto-configuration of DNS resolver in ZeroTier Central.
- • Provides a TLD. You can map subdomains just by partitioning them with a
- • Optionally parses additional
/etc/hostsformatted names and addresses into this TLD.
- • Forwards all other queries to
/etc/resolv.confresolvers, allow you to look up your TLD and
google.comwithout needing two DNS servers.
- • Portable to many operating systems and architectures by way of rust-lang.
- • Auto-detection of members list and population of:
- ○ A (IPv4) and AAAA (IPv6) records for two cases:
- • If the member’s name field is a qualified dns name it will be populated as a record.
- • In all cases, a record of
zt-.will be pointed at the IP.
- ○ PTR record (IPv4 only) with the same strategy as the A/AAAA, but names are preferred.
- ○ A (IPv4) and AAAA (IPv6) records for two cases:
How it works
zeronsd more or less does, is poll the Central API periodically and populate records in what trust-dns calls a “Catalog” and then proceeds to let trust-dns do most of the work on its own. The code footprint is quite small, and once initialized there is only one supervisory routine running (the record refresher) that does not belong to trust-dns’s crate.
To configure the listening information, we talk directly to ZeroTierOne over the privileged API, which is at
localhost:9993 and ask it to tell us its IP addresses for that network. Easy sauce.
First, to get maximum benefit, ensure your users of the networks have the Manage DNS flag turned on; this will be important as your DNS server comes live.
Generate an API token. You will need to put this in a file, or in the environment, so keep it handy.
Create a network. Make note of the Network ID: the long hexadecimal number at the top of the network’s page. You will need this to pass to
Finally, obtain a rust environment, so you can build & install
The latest release can be found this way:
cargo install zeronsd
If you want to run the bleeding edge, try:
cargo install --git https://github.com/zerotier/zeronsd --branch main
NOTE: If you are on OS X, Windows, or Linux your
zerotier-one credentials will be automatically detected. Otherwise, (e.g., FreeBSD), you will need to pass a special flag to point at the
authtoken.secret file in your ZeroTier installation.
Finally, you must run
zeronsd as root. While I would like it differently, plenty of resolvers will simply not work over anything but port 53, making root pretty much a requirement on any POSIX system.
Put your API token in a file named
zerotier-central-token and run this:
sudo zeronsd start -t zerotier-central-token<network id>
If you prefer, you can also pass
ZEROTIER_CENTRAL_TOKEN in the environment. One of these, however, is required for the daemon to start. After that, you should see something like this:
Welcome to ZeroNS! Your IP for this network: 172.27.207.100
Your IP will of course be different. 🙂 It should correspond to the IP listed in
zerotier-cli listnetworks for the network you’ve provided. Your DNS settings on the Central API will be updated, and all clients will start resolving ASAP to your DNS server.
In my case, I have the following members configured:
This allows for the following records:
% host zt-09ea1c84bd.domain 172.27.207.100 zt-09ea1c84bd.domain has address 172.27.67.27 % host test4.domain 172.27.207.100 test4.domain has address 172.27.67.27 % host 172.27.67.27 172.27.207.100 188.8.131.52.in-addr.arpa domain name pointer test4.domain.
There are a few options you can use to modify the behavior a bit. Notably:
-dlets you adjust the TLD, which defaults to
domain. Set it to any dotted combination you can think of.
-flets you add a hosts file that appends static records to the TLD. It uses /etc/hosts format, e.g.:
# <address> <host1> <host2> ... 10.0.0.1 home-router netgear-thingy 10.0.0.2 super-secret-home-server
This will result in
home-router.domain pointing to
10.0.0.1. No PTRs are generated (yet).
Things to Improve
Well, nothing’s perfect.
Does not exist yet. trust-dns supports this, so this is earmarked but we are thinking through ways to distribute certificate authorities, which is critical for these features. Stay tuned!
IPv6, 6plane, RFC4193 support
If you are a heavy IPv6 user, we could really use your help diagnosing quality-of-life issues with these features. Notably, PTR records do not work for any of these situations.
As mentioned prior, trust-dns performs quite well, and it really does most of the work once the service is configured. We can see this in performance numbers.
The benchmarking environment is slipshod; it is mostly here to say “this will perform better than mDNS”, not “OMFG THIS IS THE FASTEST DNS SERVER ON EARTH”. That said, I think we should all be comforted that this will not be your problem with this service, is my best guess.
I forked a copy of dnsbench because that was simpler than downgrading golang. Try it out!
This is running on two local nodes, so no intermediary services, just two copies of ZeroTier having a conversation. The requesting node is a 6 core, 12 thread i7-8700K mostly unloaded, running 100 threads for a million queries in total. The serving machine is a Ryzen 2800X with 8 cores and 16 threads, running a moderate workload.
zeronsd RSS usage never went beyond 10MB at the time of this benchmark.
As I think we can all see here, you’re gonna be fine.
$ export HOSTS="seafile.zerotier\ngoogle.com\n" $ export SERVER=10.147.19.234 $ dnsbench run remote -c 100 -n 1000000 -f <(echo "$HOSTS") "$SERVER" Reading names from /proc/self/fd/11 Benchmarking 10.147.19.234... # requests errors min [ p50 p95 p99 p999] max qps 76424 0 1.02 [6.02 9.72 12.41 23.77] 23.77 15284.80 75669 0 1.49 [6.18 9.99 13.74 32.37] 32.37 15133.80 76180 0 1.48 [6.19 9.85 13.03 22.94] 22.94 15236.00 76347 0 1.33 [6.14 9.64 12.59 34.90] 34.90 15269.40 72827 0 1.46 [6.33 10.78 16.74 35.49] 35.49 14565.40 77493 0 1.52 [6.06 9.66 13.27 28.49] 28.49 15498.60 76266 0 1.60 [6.04 9.95 13.66 36.54] 36.54 15253.20 76729 0 1.44 [6.07 10.11 13.16 37.58] 37.58 15345.80 76611 0 1.10 [6.13 9.89 13.07 27.33] 27.33 15322.20 74124 0 1.34 [6.26 10.26 13.71 46.96] 46.96 14824.80 76535 0 1.29 [6.11 9.67 14.08 38.14] 38.14 15307.00 78195 0 1.32 [5.91 9.66 12.57 39.22] 39.22 15639.00 80786 0 1.13 [5.85 9.40 11.44 30.08] 30.08 16157.20 Finished 1000000 requests # latency summary 1000000 0 1.02 [6.09 9.87 13.26 46.96] 46.96 15266.87 Concurrency level: 100 Time taken for tests: 65.50 seconds Completed Requests: 1000000 Failed Requests: 0 Requests per second: 15266.87 [#/sec] (mean) Time per request: 6.37 [ms] (mean) Fastest request: 1.02 [ms] Slowest request: 46.96 [ms]
This was a lot of writing and thanks for getting this far! If you’re more interested in the project, feedback and patches are always great ways to contribute. Until next time!