The first option seems bad because then the authoritative server also has a double duty as a recursive resolver for the rest of the domains.
For small LAN/homelab usage, that's not really a problem.
(It is also commonly done e.g. in Active Directory environments, where the admin points all clients to directly use an AD DC that's hosting the AD DNS zone instead of a dedicated resolver – that's unnecessary, but not strictly bad.)
There might be some edge cases (e.g. the DNSSEC AD flag), but I believe they result not from adding resolver features to an authoritative server, but from the opposite – making the machines use an authoritative server as if it were a resolver, which you are already doing.
The second is inefficient, as then the clients will have to manually resolve down the hierarchy for every new domain, which can be quite slow.
It also wouldn't work, as most clients are only stub resolvers – they will not follow referrals.
A full resolver, on the other hand, will cache referrals. The public DNS resolver that you've been using (be it the ISP's or Google's or something else) also has to resolve the hierarchy for every new domain – it certainly does not keep a full copy of the world's all DNS records – yet it is not slow, because at least the first level (TLDs) is usually cached in-memory, and there aren't that many hierarchy levels for a typical domain anyway.
Thus, the third option seems the best so far. How would I go about setting this up in BIND9, and/or should I consider an alternative solution?
In BIND9, create a type static-stub
zone pointing at the homelab auth server. (It's similar to a type forward
zone, but "static-stub" expects to be pointed directly at an authoritative server while "forward" expects to chain to another resolver. I'm not sure of the exact differences.)
For all other domains, you can specify upstream servers as forwarders{}
in global BIND configuration. This is not required – BIND9 itself is capable of chasing referrals all the way from root DNS servers (and will do so by default as long as the "root hints" pseudo-zone is configured), which really isn't slow (although certainly could be slower than forwarding requests to an upstream server with its own massive cache).
A common alternative is Unbound (which has more focus on being used as a resolver, with only minimal authoritative server features). Configuration of Unbound is similar, except for the zone type being stub
instead of "static-stub". To make Unbound forward queries, configure "."
as a "type: forward" zone.
Keep in mind that inventing custom TLDs will not play well with DNSSEC validation, as the root zone has NSEC records proving the non-existence of lab.
so you might need to explicitly exclude your domain from validation, both in the LAN resolver and sometimes in hosts themselves. Only the home.arpa.
domain is reserved for this kind of usage (having deliberately unsigned NS records).
(Alternatively, you could enable DNSSEC, sign your internal zone, and specify a custom "trust anchor" on all validating resolvers. Although DNSSEC is not really needed in a home lab, the advantage here is that adding a trust anchor might be easier than adding an exclusion, especially in BIND.)