Split DNS for macOS

David Anderson dave at natulte.net
Sat Nov 6 04:54:43 UTC 2021

On Fri, Oct 29, 2021, at 14:07, Stephen Larew wrote:
> As I understand it, under some circumstances, the Tailscale macOS app also implements split DNS in roughly the same manner as done in my patch. Namely, the tailscale app appears to use the Network Extension APIs to directly integrate with the macOS DNS resolution machinery. Perhaps the relevant difference is that tailscale approaches configuration differently (not based on wg-quick) than the WireGuard macOS app.

Hi, Tailscale person here.

Yes, the Tailscale macOS app supports configuring either split DNS. It's situationally popular for things like "direct anything under amazonaws.com to the AWS VPC internal resolver on the other side of my tunnel", which makes your AWS VMs and such magically resolve correctly without shoveling all your unrelated requests through AWS.

The semantics of NEDNSSettings I've worked out so far:
  * `searchDomains` sets interface-scoped search domains only, which appear to not be used at all for single-label DNS query expansion. IOW, it seems to do mostly nothing - or at least, I've not discovered anything it affects.
  * `matchDomains` specifies what DNS suffixes should be sent to your resolvers (specified in `servers`). Specifying a list of suffixes here implements split-DNS. Specifying "" (the empty suffix, which matches all names) captures all queries.
  * `matchDomains` _also_ installs any non-empty string you specify in the global search path.
  * `matchDomainsNoSearch` lets you make `matchDomains` be just match domains, without modifying the global search path. You don't get more granularity than that, either all `matchDomains` are search paths, or none.
  * If you want to fully capture all DNS traffic but also set some global search domains, you can list both the empty string and non-empty strings in `matchDomains`, and you'll get that behavior.
  * You only get a single set of resolver IPs. This means you can have many DNS suffixes with `matchDomains`, but all of them will go to the pool of `servers` you provided. You can't route foo.com to and bar.com to without having another intermediate proxy to break things out further.
  * Apple's DNS management service only installs the "default" resolver into the legacy /etc/resolv.conf, so a bunch of commandline tools inherited from BSD will be completely unaware of split DNS configurations, because they don't use whatever the "correct" resolution API is (I presume mach port something something).
  * Apple doesn't have a public API for reading back the OS DNS settings, because they don't want other applications poorly reimplementing the OS's algorithm for selecting among contributed configuration. There is presumably an undocumented API that scutil et al. can use to read things out, but I've not dug into that at all.

Regarding upstreaming: OS-level DNS capabilities vary wildly across linux, apple and windows, and across versions of same (e.g. Windows 8+ can do split DNS, Windows 7 cannot - but WSL/WSL2 can't on any version to date). I can ramble at length about each OS if desired, but the bottom line is "send all DNS to these servers" is the only configuration that can be implemented reliably on all of them.

So, the question would be: do you want upstream WireGuard applications to have consistent behavior on all platforms? If so, you have to either forego split DNS, or do a lot of work to polyfill missing features on each platform (Tailscale's attempt at this is https://github.com/tailscale/tailscale/tree/main/net/dns). Or expose the uneven feature surface to the user, and delegate the pain of non-portability to them.

- Dave

More information about the WireGuard mailing list