25

systemd-resolved is a daemon that, among other things, acts as a DNS server by listening IP address 127.0.0.53 on the local loopback interface.

I would like to let the daemon listen to another interface. My use-case is to expose it to docker containers, so that docker containers share the DNS caching provided by systemd-resolved. I know how to configure the host as a DNS server for docker containers, but at least by default, systemd-resolved rejects these DNS queries because they are not coming from the loopback interface, but from the docker bridge interface.

With dnsmasq (a tool similar to systemd-resolved), I did this by adding listen-address=172.17.0.1 to the configuration file. Unfortunately, I couldn't find a systemd-resolved equivalent.

Since systemd-resolved is the default at least on Ubuntu 18.04, I would like a solution that works in this configuration.

Is there a way to configure which interface systemd-resolved listens on?

3
  • 1
    What is your use-case? I mean, why are you employing systemd-resolved as a stub resolver? There are any number of established, purpose-made alternatives, including but not limited to bind, dnsmasq, unbound, etc. Note that this is not a rant or mindless hate on the systemd suite. I'd just like to know what prompted you to pick this particular service. Is there any functionality the others don't offer?
    – Nubarke
    Commented May 30, 2018 at 15:42
  • 2
    My use case is to have docker that "just works" on my machine and on typical people's machine (e.g. my students, as I'm a teacher). I didn't pick systemd-resolved: my distro (Ubuntu) picked it for me when I upgraded (to 18.04). I can uninstall systemd-resolved and re-install dnsmasq (which I had before), but that's a rather intrusive solution, and in particular not a solution I'd recommend lightly to anyone (for example, I tried installing dnsmasq without uninstalling systemd-resolved. My package manager happily accepted and then my machine went berserk eating 100% CPU for nothing ...). Commented May 31, 2018 at 8:04
  • 1
    Thanks for the reply. So I guess the question changes from "why did you pick systemd-resolved" to "why did the Ubuntu mantainers pick systemd-resolved". It seems clear that, at the very least, packages dnsmasq and systemd-resolved should have a breaks relationship. And again, why did the distro maintainers make that choice? Seems rather intrusive.
    – Nubarke
    Commented Jun 1, 2018 at 8:49

3 Answers 3

15

You can't. As cristian-rodríguez mentioned above, it was strictly designed to provide services to loopback only.

Not even an alternative solution using net.ipv4.conf.all.route_localnet=1 + iptables NAT (such as https://serverfault.com/questions/211536/iptables-port-redirect-not-working-for-localhost), https://superuser.com/questions/594163/how-do-i-route-a-port-range-in-a-linux-host-to-a-guest-vm), and https://stackoverflow.com/questions/18580637/iptables-redirect-from-external-interface-to-loopbacks-port) will work, since systemd-resolve explicitly inspects if the destination is outside the loopback network. See the code below for static void dns_stub_process_query(Manager *m, DnsStream *s, DnsPacket *p)

    if (in_addr_is_localhost(p->family, &p->sender) <= 0 ||
        in_addr_is_localhost(p->family, &p->destination) <= 0) {
            log_error("Got packet on unexpected IP range, refusing.");
            dns_stub_send_failure(m, s, p, DNS_RCODE_SERVFAIL, false);
            goto fail;
    }

A workaround is to use socat to listen at your docker interface, and forward it to systemd-resolved. The line below does the trick. If required, alter it to listen to TCP:

socat UDP-LISTEN:53,fork,reuseaddr,bind=172.17.0.1 UDP:127.0.0.53:53
8
  • 12
    Apparently this changed. Now we seem to have the DNSStubListenerExtra directive at /etc/systemd/resolved.conf that Takes an IPv4 or IPv6 address to listen on. freedesktop.org/software/systemd/man/…
    – TCB13
    Commented Nov 3, 2020 at 23:35
  • @TCB13 Can't get this to work on Debian Buster/Ubuntu 20.04 LTS. Switching to unbound probably
    – Ray Foss
    Commented Feb 6, 2021 at 16:10
  • 1
    @RayFoss that's because systemd/resolved in Debian is an old version that doesn't support DNSStubListenerExtra yet. Yes, I'm also waiting for it ;)
    – TCB13
    Commented Feb 6, 2021 at 20:08
  • @TCB13 it runs on 127.0.0.53 which makes it easy to ignore when I stalling unbound/DNSMasq. Turns out my issue wasn't DNS related, instead my ISP was doing Deep packet inspection and blocking domains from CDN's with a low reputation. Incredibly annoying what it took to figure that out.
    – Ray Foss
    Commented Feb 7, 2021 at 0:39
  • @RayFoss when your ISP does DPI you know it's time to get a VPN.
    – TCB13
    Commented Feb 7, 2021 at 10:24
13
+100

Resolved is not intended nor designed for your use-case, but to provide services in the local loopback, thus the listen address is hardcoded.

1
13

Exactly the desired configuration of listen-addresses is freely possible since systemd-resolved version 247 (commit 1f05101f and follow-ups) by setting DNSStubListenerExtra.

Address, port, and protocol (tcp/udp) is configurable multiple times. It's even possible to disable the default listener at (tcp/udp) 127.0.0.53:53 by setting DNSStubListener to false.

The systemd-resolved configuration-file /etc/systemd/resolved.conf shows this in it's comments – for instance in ubuntu impish (21.10).

# systemd --version
systemd 248 (248.3-1ubuntu8)
...

# man resolved.conf
...
DNSStubListener=
       Takes a boolean argument or one of "udp" and "tcp". If "udp", a DNS stub resolver will listen for UDP requests on address 127.0.0.53 port 53. If "tcp",
       the stub will listen for TCP requests on the same address and port. If "yes" (the default), the stub listens for both UDP and TCP requests. If "no", the
       stub listener is disabled.

       Note that the DNS stub listener is turned off implicitly when its listening address and port are already in use.

DNSStubListenerExtra=
   Takes an IPv4 or IPv6 address to listen on. The address may be optionally prefixed with a protocol name ("udp" or "tcp") separated with ":". If the
   protocol is not specified, the service will listen on both UDP and TCP. It may be also optionally suffixed by a numeric port number with separator ":".
   When an IPv6 address is specified with a port number, then the address must be in the square brackets. If the port is not specified, then the service
   uses port 53. Note that this is independent of the primary DNS stub configured with DNSStubListener=, and only configures additional sockets to listen
   on. This option can be specified multiple times. If an empty string is assigned, then the all previous assignments are cleared. Defaults to unset.

   Examples:

       DNSStubListenerExtra=192.168.10.10
       DNSStubListenerExtra=2001:db8:0:f102::10
       DNSStubListenerExtra=192.168.10.11:9953
       DNSStubListenerExtra=[2001:db8:0:f102::11]:9953
       DNSStubListenerExtra=tcp:192.168.10.12
       DNSStubListenerExtra=udp:2001:db8:0:f102::12
       DNSStubListenerExtra=tcp:192.168.10.13:9953
       DNSStubListenerExtra=udp:[2001:db8:0:f102::13]:9953
...

This is an explicit answer for easy reference. @TCB13 already stated it as a comment.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .