From 502960d7967f39c6e21617e725960aebf890dbb6 Mon Sep 17 00:00:00 2001 From: Scott Kuhl Date: Sun, 25 Oct 2020 12:29:32 -0400 Subject: [PATCH] Intercept DNS requests sent by systemd-resolved. Previously, we would find DNS servers we wish to intercept traffic on by reading /etc/resolv.conf. On systems using systemd-resolved, /etc/resolv.conf points to localhost and then systemd-resolved actually uses the DNS servers listed in /run/systemd/resolve/resolv.conf. Many programs will route the DNS traffic through localhost as /etc/resolv.conf indicates and sshuttle would capture it. However, systemd-resolved also provides other interfaces for programs to resolve hostnames besides the localhost server in /etc/resolv.conf. This patch adds systemd-resolved's servers into the list of DNS servers when --dns is used. Note that sshuttle will continue to fail to intercept any traffic sent to port 853 for DNS over TLS (which systemd-resolved also supports). For more info, see: sshuttle issue #535 https://www.freedesktop.org/software/systemd/man/systemd-resolved.service.html https://github.com/systemd/systemd/issues/6076 --- docs/manpage.rst | 6 ++++-- sshuttle/helpers.py | 45 +++++++++++++++++++++++++++++++++++++++------ 2 files changed, 43 insertions(+), 8 deletions(-) diff --git a/docs/manpage.rst b/docs/manpage.rst index 9c59c17..b5adfb6 100644 --- a/docs/manpage.rst +++ b/docs/manpage.rst @@ -104,10 +104,12 @@ Options Capture local DNS requests and forward to the remote DNS server. All queries to any of the local system's DNS - servers (/etc/resolv.conf) will be intercepted and + servers (/etc/resolv.conf and, if it exists, + /run/systemd/resolve/resolv.conf) will be intercepted and resolved on the remote side of the tunnel instead, there using the DNS specified via the :option:`--to-ns` option, - if specified. + if specified. Only plain DNS traffic sent to these servers + on port 53 are captured. .. option:: --ns-hosts= diff --git a/sshuttle/helpers.py b/sshuttle/helpers.py index a62bea9..5d61b43 100644 --- a/sshuttle/helpers.py +++ b/sshuttle/helpers.py @@ -49,12 +49,45 @@ class Fatal(Exception): def resolvconf_nameservers(): - lines = [] - for line in open('/etc/resolv.conf'): - words = line.lower().split() - if len(words) >= 2 and words[0] == 'nameserver': - lines.append(family_ip_tuple(words[1])) - return lines + """Retrieves a list of tuples (address type, address as a string) that + the current system uses to resolve hostnames from /etc/resolv.conf + and possibly other files. + """ + + # Historically, we just needed to read /etc/resolv.conf. + # + # If systemd-resolved is active, /etc/resolv.conf will point to + # localhost and the actual DNS servers that systemd-resolved uses + # are stored in /run/systemd/resolve/resolv.conf. For programs + # that use the localhost DNS server, only reading /etc/resolv.conf + # is sufficient. However, resolved provides other ways of + # resolving hostnames (such as via dbus) that may not route + # requests through localhost. So, we retrieve a list of DNS + # servers that resolved uses so we can intercept those as well. + # + # For more information about systemd-resolved, see: + # https://www.freedesktop.org/software/systemd/man/systemd-resolved.service.html + # + # On machines without systemd-resolved, we expect opening the + # second file will fail. + files = ['/etc/resolv.conf', '/run/systemd/resolve/resolv.conf'] + + nsservers = [] + for f in files: + this_file_nsservers = [] + try: + for line in open(f): + words = line.lower().split() + if len(words) >= 2 and words[0] == 'nameserver': + this_file_nsservers.append(family_ip_tuple(words[1])) + debug2("Found DNS servers in %s: %s\n" % + (f, [n[1] for n in this_file_nsservers])) + nsservers += this_file_nsservers + except OSError as e: + debug3("Failed to read %s when looking for DNS servers: %s\n" % + (f, e.strerror)) + + return nsservers def resolvconf_random_nameserver():