Configure systemd-resolved to use a specific DNS nameserver for a given domain
Use case
Given
- I use a VPN to connect to my work network
- I'm on a Linux computer that uses systemd-resolved
- I have a work domain called
example.com
example.com
is hosted by both public and private DNS nameservers- Both public and private nameservers claim to be authoritative for
example.com
- There are no public hosts in
example.com
- The public resolvers for
example.com
resolve all queries to a parked hosting webpage - The private resolvers for
example.com
contain all correct DNS records for private hosts
I need to
- Resolve private hosts in
example.com
when connected to VPN
(Note that this should also work for pointing DNS-blocked domains at different, non-blocked nameservers)
Solution
systemd-resolved now has the ability to specify nameservers for specific domains. Until recently this was not the case, systemd-resolved leaned on NetworkManager, which used dnsmasq for this purpose.
If you were already doing something like this to accomplish this task, first undo all of that. We're not going to use NetworkManager/dnsmasq.
In your systemd-resolved config, which for me is at /etc/systemd/resolved.conf
(Fedora), make sure you have this (assuming example.com
private nameservers are 10.0.0.1
and 10.0.0.2
)
[Resolve]
DNS=10.0.0.1 10.0.0.2
Domains=~example.com
Note the tilde, that makes systemd-resolved do something special. According to the man page:
Specified domain names may optionally be prefixed with "~". In this case they do not define a search path, but preferably direct DNS queries for the indicated domains to the DNS servers configured with the system DNS= setting (see above), in case additional, suitable per-link DNS servers are known.
Restart systemd-resolved and you should be in business.
But setting
DNS=10.0.0.1 10.0.0.2
is a global configuration, its true for all queries.We can check this by doing some tcpdumping and running
drill
queries.Here is my config:
And
resolvectl
shows:Now I fire off a drill query:
$ drill mer-prd-rdsh14.tp.private
And see it come though on my wireguard interface:
Now I query some other domain:
$ drill porkiepie.com
And the query still goes to my globally set resolver:
10.253.253.253
If I drop my routes to
10.253.253.253
, and try to query:$ drill exmaple.com
We still try to resolve against
10.253.253.253
, fail and try217.169.20.21
which is the resolved I get from my home router for the wifi link: