84

I'm connected to local area network with access to the Internet through gateway. There is DNS server in local network which is capable of resolving hostnames of computers from local network.

I would like to configure systemd-resolved and systemd-networkd so that lookup requests for local hostnames would be directed (routed) exclusively to local DNS server and lookup requests for all other hostnames would be directed exclusively to another, remote DNS server.

Let's assume I don't know where the configuration files are or whether I should add more files and require their path(s) to be specified in the answer.

Peter Crotty
  • 105
  • 2
Piotr Dobrogost
  • 4,116
  • 5
  • 35
  • 46
  • Seems such a normal request. I hope networkd lives for a very long time before Linux networking gets fiddled with again – volvox Jul 11 '23 at 16:40

3 Answers3

78

In the configuration file for local network interface (a file matching the name pattern /etc/systemd/network/*.network) we have to either specify we want to obtain local DNS server address from DHCP server using DHCP= option:

[Network]
DHCP=yes

or specify its address explicitly using DNS= option:

[Network]
DNS=10.0.0.1

In addition we need to specify (in the same section) local domains using Domains= option

Domains=domainA.example domainB.example ~example

We specify local domains domainA.example domainB.example to get the following behavior (from systemd-resolved.service, systemd-resolved man page):

Lookups for a hostname ending in one of the per-interface domains are exclusively routed to the matching interfaces.

This way hostX.domainA.example will be resolved exclusively by our local DNS server.

We specify with ~example that all domains ending in example are to be treated as route-only domains to get the following behavior (from description of this commit) :

DNS servers which have route-only domains should only be used for the specified domains.

This way hostY.on.the.internet will be resolved exclusively by our global, remote DNS server.

Note

Ideally, when using DHCP protocol, local domain names should be obtained from DHCP server instead of being specified explicitly in configuration file of network interface above. See UseDomains= option. However there are still outstanding issues with this feature – see systemd-networkd DHCP search domains option issue.

We need to specify remote DNS server as our global, system-wide DNS server. We can do this in /etc/systemd/resolved.conf file:

[Resolve]
DNS=8.8.8.8 8.8.4.4 2001:4860:4860::8888 2001:4860:4860::8844

Don't forget to reload configuration and to restart services:

$ sudo systemctl daemon-reload
$ sudo systemctl restart systemd-networkd
$ sudo systemctl restart systemd-resolved

Caution!

Above guarantees apply only when names are being resolved by systemd-resolved – see man page for nss-resolve, libnss_resolve.so.2 and man page for systemd-resolved.service, systemd-resolved.

See also:

References:

gps
  • 103
  • 3
Piotr Dobrogost
  • 4,116
  • 5
  • 35
  • 46
  • 7
    Have you considered not using `.local` in this example? Certainly with avahi, this was supposed to be reserved for MDNS and misusing it was a big no-no. It would be clearer to me to use `example.com` or [.example](https://en.wikipedia.org/wiki/.example). – sourcejedi May 08 '18 at 17:17
  • 1
    @sourcejedi For reference `.local` is defined as special domain in RFC 6762 – *Multicast DNS* in section [Multicast DNS Names](https://tools.ietf.org/html/rfc6762#section-3). Thanks, fixed. – Piotr Dobrogost May 09 '18 at 07:48
  • 2
    The network configuration file must be named something that matches `/etc/systemd/network/*.network`. The value of `*` doesn't appear to matter. You use a `[Match]` section within each file to limit which network interface(s) it applies to. – gps Apr 05 '20 at 22:56
  • 1
    @gps, moreover, without `[Match]` section, file seems to be ignored altogether – GreenScape Nov 19 '20 at 11:34
  • You *have* to configure the `[Match]` section somehow, otherwise you get something to the effect of: ``` /etc/systemd/network/eth0.network: No valid settings found in the [Match] section, ignoring file. To match all interfaces, add Name=* in the [Match] section. ``` @PiotrDobrogost I would add that to my answer, if I were you. – Lethargos Nov 16 '21 at 19:03
  • 3
    Why is `Domains=domainA.example domainB.example ~example` needed - wouldn't `Domains=~example` provide the same? – laur Jan 17 '22 at 16:21
  • @laur Yes, it would provide the same as long as routing of DNS queries is concerned. The reason why they are listed in addition to `~example` is explained in a note further in the answer. Strictly speaking (answering only the question as stated) local domains do not need to be listed in `Domains` directive. However without `UseDomains` directive working at the time (as explained in the note) I kind of treated listing local domains in `Domains` directive as the essential part of preferred DNS configuration as otherwise single-label hostnames wouldn't be resolved against these local domains. – Piotr Dobrogost Jan 16 '23 at 16:04
7

Just to expand on @piotrDobrogost 's excellent answer, don't forget to config /etc/nsswitch.conf to use systemd-resolved as a DNS resolution source. Your hosts directive should look as follows for your particular use case:

/etc/nsswitch.conf

hosts:  files resolve dns

So if you limit the resolution to only those domains specified in the Domains directive in /etc/systemd/resolved.conf as Piotr details above, DNS should next be consulted in the order of name resolution sources specified /etc/nsswitch.conf when domains are NOT found in Domains directive:

The following link references the requirement to specify resolve in /etc/nsswitch.conf so systemd-resolved is consulted during name resolution:

https://github.com/systemd/systemd/issues/940

SystemD documentation I've found to be dire. I had to piece together an understanding from multiple links, including Piotr's answer above ;-)

F1Linux
  • 2,286
  • 1
  • 16
  • 28
  • 1
    When using [recommended](https://www.freedesktop.org/software/systemd/man/systemd-resolved.service.html#/etc/resolv.conf) mode of operation of _systemd-resolved_ where `/etc/resolve.conf` is a symlink to `/run/systemd/resolve/stub-resolv.conf` file which in turn contains address of _systemd-resolve's_ DNS stub resolver there's no need to place `resolve` directive in `/etc/nsswitch.conf` file as DNS requests will be directed (due to the standard `nss-dns` directive) to stub resolver which acts according to _systemd-resolved's_ rules. – Piotr Dobrogost Oct 13 '19 at 14:44
  • @PiotrDobrogost How could you control the order sources of DNS resolution are consulted without using `/etc/nsswitch.conf``? In the specimen config above, `/etc/hosts` ("***files***") would be checked for static **IP:name** mappings and if none found, the systemd-resolved stub resolved would next be consulted. I can't see how it would be possible to therefore stage sources of DNS resolution without using `/etc/nsswitch.conf`. Am I missing a trick here? – F1Linux Oct 13 '19 at 14:58
  • I'm not saying `/etc/nsswitch.conf` is not needed. I'm saying that when one uses _systemd-resolved's_ stub DNS resolver then it's enough to have `dns` directive listed in `hosts:` line (presumably after `file` directive). There is no need for `resolve` directive there as it's the stub resolver that is the *entry point* to the _systemd-resolved's_ logic and not the `nss-resolve` plug-in module... – Piotr Dobrogost Oct 13 '19 at 15:21
  • ... In other words you can *reach* _systemd-resolved's_ logic either through `resolve` directive ➟ _nss-resolve_ NSS plug-in module ➟ _systemd-resolved_ or through `dns` directive ➟ _nss-dns_ NSS plug-in module ➟ _systemd-resolved's_' stub DNS resolver ➟ _systemd-resolved_ – Piotr Dobrogost Oct 13 '19 at 15:21
  • @PiotrDobrogost I think I arrived at the `files` then `resolve` thingy in `/etc/nsswitch.conf` from the 2nd part of your question. Re-reading it, looks like you were just talking about checking the local cache for an **IP:name** mapping then reaching-out to a forwarder if not found. I generally set `files` first DNS source of resolution to bypass DNS to allow me to test and not hit the production hosts- T – F1Linux Oct 13 '19 at 15:32
  • I don't blame you as the `resolve` directive was in the past the recommended way of using _systemd-resolved_ logic. As there were differences in the behavior when using `resolve` directive and when querying DNS server(s) listed in `/etc/resolv.conf` directly which lead to much confusion later the recommended way of using _systemd-resolved_ logic was changed to using stub DNS resolver. See for instance [nsswitch.conf doesn't specify 'resolve' to support systemd-resolved](https://bugs.launchpad.net/ubuntu/+source/glibc/+bug/1769016). – Piotr Dobrogost Oct 13 '19 at 15:58
3

If you are doing this because you have an connection configured with OpenVPN, you need to use https://github.com/jonathanio/update-systemd-resolved as per https://wiki.archlinux.org/index.php/OpenVPN#The_update-systemd-resolved_custom_script

Specifically, once, you have the update-systemd-resolved script installed and active in your OpenVPN client config, you'll also add dhcp-option DOMAIN-ROUTE yourdomain.com to the same client config.

You should see the following output from OpenVPN:

<14>Apr 22 16:10:31 update-systemd-resolved: Link 'tun0' coming up
<14>Apr 22 16:10:31 update-systemd-resolved: Adding DNS Routed Domain yourdomain.com
<14>Apr 22 16:10:31 update-systemd-resolved: Adding DNS Domain yourdomain.com
<14>Apr 22 16:10:31 update-systemd-resolved: Adding IPv4 DNS Server 192.168.XYZ.XYZ
<14>Apr 22 16:10:31 update-systemd-resolved: SetLinkDNS(640 1 2 4 192 168 XYZ XYZ)
<14>Apr 22 16:10:31 update-systemd-resolved: SetLinkDomains(640 2 yourdomain.com false yourdomain.com true)

And you can verify the DNS configuration with resolvectl status:

Link 3 (wlp0s....)
      Current Scopes: DNS
DefaultRoute setting: yes
       LLMNR setting: yes
MulticastDNS setting: no
  DNSOverTLS setting: no
      DNSSEC setting: no
    DNSSEC supported: no
  Current DNS Server: 192.168.XYZ.XYZ
         DNS Servers: 192.168.XYZ.XYZ
          DNS Domain: ~.
                      lan
Link 640 (tun0)
      Current Scopes: DNS
DefaultRoute setting: no
       LLMNR setting: yes
MulticastDNS setting: no
  DNSOverTLS setting: no
      DNSSEC setting: no
    DNSSEC supported: no
  Current DNS Server: 192.168.XYZ.XYZ
         DNS Servers: 192.168.XYZ.XYZ
          DNS Domain: ~yourdomain.com
dols
  • 141
  • 2
  • really helpful indeed. once the openvpn has been setup this is a really simple addition. thanks – nass Feb 18 '21 at 16:14