Ambiguous hostname queries
Introduction: mDNS resolution and introduction of a VPN
As the number of computers I maintain in my personal network, I had to setup multicast DNS (mDNS) to address them automatically. Networkd or avahi can be configured to advertise and/or resolve mDNS requests. ".local" is the default mDNS extension so once mDNS enabled, machines can connect to "desktop.local" or "laptop.local" instead of specifying e.g, the laptop's IP. While outside - e.g., in a coffee shop - I still want access to some of these machines, for instance to run inference against my llama server with a "powerful" GPU. This led me to create a wireguard VPN where a VPS acts as a relay.
Wouldn't it be wonderful if I could refer to these hosts by just their hostname and have the resolution
system auto select the correct domain instead of typing it myself ?
For instance, I type ping desktop and my system would return the IP of either desktop.local if I am at home or desktop.vpn if I am on the move.
I had already a solution for SSH but wanted to generalize it to any host DNS query.
This post explains how I solved the problem. It is more convoluted the journey rabbit hole I went through.
An SSH solution only
SSH client configuration lets you define per-destination settings. There are
various filters like localnetwork to match a specific network.
Since I connect from similarly addressed private networks, I preferred the
CanonicalizeHostname approach:
Match host=router
AddKeysToAgent yes
CanonicalizeHostname yes
CanonicalDomains local vpn
identitiesOnly yes
This lets me run ssh router and SSH will try to connect to router.local then
router.vpn. Here a blog with more details.
First attempt at a system-level (m)DNS resolving solution
let's have systemd-resolved resolve both mDNS and DNS
To have a similar behavior at the system level, I thought of using the search
setting of /etc/resolv.conf: the
"search" section is a list of domains to try resolving if the query contains
fewer dots than the ndots setting (defaulting to 1). E.g., with search example.com internal.lan in resolv.conf, your various resolvers getent toto will try
toto.example.com and then toto.internal.lan.
My idea was to set search local vpn to resolve desktop.local first, and fall
back to desktop.vpn. Keep in mind that mDNS (part of the "zeroconf" concept) works as a different protocol than DNS. It consists in multicast requests on your network to autodiscover machines and services. A popular publisher/resolver is avahi but systemd-resolved can be configured to do it, as it is on my machine. So I was curious to see whether ping desktop would resolve to desktop.local IP. Turns out it does not and the resolved manpage says this:
Note that configuring the MulticastDNS domain "local" as search or routing domain has the effect of routing lookups for this domain to classic unicast DNS. This may be used to provide compatibility with legacy installations that use this domain in a unicast DNS context, against the IANA assignment of this domain to pure MulticastDNS purposes. Search and routing domains are a unicast DNS concept, they cannot be used to resolve single-label lookups via MulticastDNS.
Previous to networkd, I would use avahi to handle mDNS. So I thought "let's put it back" !
back to avahi & nsswitch.conf
GNU/Linux has a file /etc/nsswitch.conf (nss stands for Name Service Switch) that configures the sources the GNU C library must query to obtain name-service information.
It looks like this:
passwd: files systemd
group: files [success=merge] systemd
shadow: files systemd
sudoers: files
hosts: mymachines resolve [!UNAVAIL=return] files myhostname dns mdns4
networks: files
ethers: files
services: files
protocols: files
rpc: files
subuid: files
subgid: files
One can query the various databases through getent:
$ LANG=C getent --help
Usage: getent [OPTION...] database [key ...]
Get entries from administrative database.
Supported databases:
ahosts ahostsv4 ahostsv6 aliases ethers group gshadow hosts initgroups
netgroup networks passwd protocols rpc services shadow
https://github.com/avahi/nss-mdns.git
# Let's put this to the test
$ getent passwd teto
teto:x:1000:100::/home/teto:/run/current-system/sw/bin/fish
# what about network
$ getent ahosts router.local
192.168.1.11 STREAM router.local
192.168.1.11 DGRAM
192.168.1.11 RAW
I will let you go through the manpages starting with nss to fulfill your curiosity.
Back to our goal of having the label desktop resolved to either desktop.local
or desktop.vpn: if resolved does not want to resolve the local domain via
mDNS, let's insert mDNS resolution in /etc/nsswitch.conf
hosts: mymachines mdns4_minimal [NOTFOUND=return] resolve [!UNAVAIL=return] files myhostname dns
This is achieved on nixos by setting
services.avahi.enable = true.
The nixos module also exposes the C library nss-mdns since this is now nss work: glibc queries similarly resolved via the nss-resolve library.
Surely now ping desktop is going to work ! Wait why not ? Turns out nss-mdns
stopped looking at the search setting from resolv.conf !
I first learned it through this guy (thanks for posting a patch), with the
official changelog listing that it was disabled by
default in 2007
and totally removed in 2018.
I considered patching the library but only briefly, before getting reasonable again. Turns out multicast queries have also some drawbacks:
- no negative resolutions so you have to wait for a timeout.
.localbeing reserved, program's behavior change or becomes an unknown when using it, which is why the Internet often recommends to use another domain (which requires further setup, please no !)- having more than one label needs further tweaking as is highlighted by nss-mdns's doc, e.g. resolving toto.desktop.local won't work out of the box.
- nix overrides
nsswitch.confduring its operation tohosts: files dns(see the last section for more details) would not support mDNS. And I would like to refer to my nix remote builders with the same pattern for consistency
So at this point, I was looking for a pure DNS solution.
Find a DNS server that can handle multicast request as DNS
I asked an LLM to list powerful linux DNS solvers that could handle solving indifferently between multicast and true DNS requests.
coredns turned up with a plugin to do that.
mDNS proxies such as [this one][mdns-proxy got suggested as well. This could be an interesting solution to resolve multicast addresses from outside via the vpn, which is not my immediate goal. Plus it is ruby, which I haven't done in a long time.
While exploring these solutions, the Internet showed me a side .
Circumventing the issue: when the ISP provides a workaround
While googling the subject, I found out that 1 or 2 months earlier, my "freebox" (the name of the router delivered by my ISP) had received an upgrade to expose the machines on the network via DNS under the domain ".home".
So switching search local vpn to search home vpn is enough to achieve what I
want !
The option is set on nixos with:
networking.search = [ "home" "vpn" ];
which means that now ping laptop will now first resolve laptop.home and
then laptop.vpn !
Conclusion
I scrapped my initial idea of using mDNS along the way and if this what you were looking for, sorry I have not fully answered your question. Seems like coredns is the answer but I have not tested it. I am happy with my setup and left a few links + note on the nix cache after.
Related links I
- a clear [question][superuser-mdns] question and analysis on superuser.
- a short blog article mentioning .home.arpa. instead of .home
Extra section about nix name resolution
While testing my mDNS configuration, I noticed nix was not able to resolve my
binary cache via desktop.local even when all other tools worked. Intrigued, I
dug deeper and funnily enough nix overrides the hosts database to hosts: files dns.
No mDNS there ! There is an interesting unrelated project that lets you
auto discover nix caches on your mDNS network called
nix-cache-beacon. Works well enough here !