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
This commit is contained in:
Scott Kuhl 2020-10-25 12:29:32 -04:00
parent 5c8c707208
commit 502960d796
2 changed files with 43 additions and 8 deletions

View File

@ -104,10 +104,12 @@ Options
Capture local DNS requests and forward to the remote DNS Capture local DNS requests and forward to the remote DNS
server. All queries to any of the local system's 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 resolved on the remote side of the tunnel instead, there
using the DNS specified via the :option:`--to-ns` option, 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=<server1[,server2[,server3[...]]]> .. option:: --ns-hosts=<server1[,server2[,server3[...]]]>

View File

@ -49,12 +49,45 @@ class Fatal(Exception):
def resolvconf_nameservers(): def resolvconf_nameservers():
lines = [] """Retrieves a list of tuples (address type, address as a string) that
for line in open('/etc/resolv.conf'): the current system uses to resolve hostnames from /etc/resolv.conf
words = line.lower().split() and possibly other files.
if len(words) >= 2 and words[0] == 'nameserver': """
lines.append(family_ip_tuple(words[1]))
return lines # 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(): def resolvconf_random_nameserver():