如何强制 Ubuntu 22.04 SERVER 仅使用 /etc/systemd/resolved.conf 中列出的 DNS 服务器(不是我的路由器的 DNS)并使用 DNS-over-TLS?

Ubu*_*ser 4 server vpn networking dns 22.04

我已将这些行添加到没有桌面的 Ubuntu 22.04 服务器安装中的 /etc/systemd/resolved.conf - 我强调这一点,因为我无法使用在 Ubuntu 中设置网络选项的常规桌面方法:

DNS=9.9.9.9 149.112.112.112 1.1.1.1 1.0.0.1
DNSOverTLS=yes
Run Code Online (Sandbox Code Playgroud)

这工作得很好,除了所有连接都应该通过标识为 tun0 的 VPN 隧道。因此,在上述配置到位后,如果我这样做

sudo tcpdump -ni tun0 -p port 53 or port 853
Run Code Online (Sandbox Code Playgroud)

在一个终端窗口中尝试从另一个终端窗口 ping google.com,我看到几个请求,但它们都在端口 853 上发出。太棒了,所以 DNS-over-TLS 正在工作。问题是如果我跑

sudo tcpdump -ni enp0s10 -p port 53 or port 853
Run Code Online (Sandbox Code Playgroud)

我看到一些行显示它也在尝试访问我的路由器 192.168.1.1,仍在尝试使用端口 853,但看起来它尝试了四次去那里。这不太好。我不希望它尝试使用我的路由器的 DNS,所有流量都应该通过隧道。

我尝试了另一种方法,即临时重命名 /etc/resolv.conf (这是到其他地方的符号链接)并将其替换为实际文本文件(不是符号链接)的版本,我在其中放置了这些行:

nameserver 9.9.9.9
nameserver 149.112.112.112
nameserver 1.1.1.1
options use-vc edns0 trust-ad
search localdomain
Run Code Online (Sandbox Code Playgroud)

除了名称服务器链接之外,我唯一更改的是在选项行中添加“use-vc”,因为我读到这是为了启用 DNS-over-TLS。当我这样做时,当我发出 DNS 请求时,没有流量到达本地网络接口,但请求使用端口 53 而不是端口 853 发出 tun0,因此它没有使用 DNS-over-TLS。所以 use-vc 选项没有被遵守。

我在过去的五个小时里一直在阅读页面,但找不到解决方案,主要是因为有关该主题的大多数页面要么假设您正在运行桌面版本的 Ubuntu,要么它们似乎适用于不同版本的 Ubuntu,其中做得有点不同。如何才能使 DNS 请求仅发出 tun0,但仍使用 DNS-over-TLS?

编辑回答问题 - 我重新使用静态 resolv.conf,因为这是我确定流量会流出 VPN 的唯一方法,但如上所述,DNS 请求是在端口 53 上发出的,而不是端口 853 上的。请记住,这是resolvctl status的输出:

Global
       Protocols: -LLMNR -mDNS +DNSOverTLS DNSSEC=no/unsupported
resolv.conf mode: foreign
     DNS Servers: 9.9.9.9 149.112.112.112 1.1.1.1 1.0.0.1

Link 2 (enp0s10)
Current Scopes: DNS
     Protocols: +DefaultRoute +LLMNR -mDNS +DNSOverTLS DNSSEC=no/unsupported
   DNS Servers: 192.168.1.1
    DNS Domain: localdomain

Link 3 (tun0)
Current Scopes: none
     Protocols: -DefaultRoute +LLMNR -mDNS +DNSOverTLS DNSSEC=no/unsupported

Link 4 (wlp5s0)
Current Scopes: none
     Protocols: -DefaultRoute +LLMNR -mDNS +DNSOverTLS DNSSEC=no/unsupported
Run Code Online (Sandbox Code Playgroud)

这是 ip a 的输出(MAC 地址经过编辑):

1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
2: enp0s10: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether xx:xx:xx:xx:xx:xx brd ff:ff:ff:ff:ff:ff
    inet 192.168.1.120/24 metric 100 brd 192.168.1.255 scope global dynamic enp0s10
       valid_lft 6592sec preferred_lft 6592sec
3: tun0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UNKNOWN group default qlen 500
    link/none
    inet 10.12.216.2/24 scope global tun0
       valid_lft forever preferred_lft forever
4: wlp5s0: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default qlen 1000
    link/ether xx:xx:xx:xx:xx:xx brd ff:ff:ff:ff:ff:ff
Run Code Online (Sandbox Code Playgroud)

我不确定 netplan 或 systemd,但我假设 netplan 因为有一个文件 /etc/netplan/00-installer-config.yaml 其中包含以下内容:

# This is the network config written by 'subiquity'
network:
  ethernets:
    enp0s10:
      dhcp4: true
  version: 2
Run Code Online (Sandbox Code Playgroud)

但是请记住我所说的将 resolv.conf 制作成静态文件。

VPN 是 OpenVPN,它连接到远程位置的 OpenVPN 服务器。为了使它工作,我必须在每次启动时运行它:

/usr/sbin/openvpn --client --config /etc/openvpn/client/xBuQqzYnLLTezXRvrOofMBGgYAEgcY.ovpn
Run Code Online (Sandbox Code Playgroud)

出于安全原因,我真的不想发布整个 .ovpn 文件,而且我不确定它是否会有帮助,但这里是顶部部分,其中有一些修订:

client
dev tun
proto udp
remote (redacted)
nobind
remote-cert-tls server
tls-version-min 1.2
verify-x509-name (redacted)
cipher AES-256-CBC
auth SHA256
auth-nocache
askpass (redacted)
verb 3
script-security 2
up /etc/openvpn/update-resolv-conf
down /etc/openvpn/update-resolv-conf
redirect-gateway
dhcp-option DNS 10.12.216.2
Run Code Online (Sandbox Code Playgroud)

之后是各种密钥和证书。

我真的不认为问题出在 .ovpn 配置中,我认为如果您使用默认的符号链接 resolv.conf,它会首先尝试访问路由器(192.168.1.1)。如果您想知道该版本的 resolv.conf 是什么样子,它是

nameserver 127.0.0.53
options edns0 trust-ad
search localdomain
Run Code Online (Sandbox Code Playgroud)

我相信这是所有 Ubuntu 版本的默认设置。现在我不确定 127.0.0.53 到底是什么,我猜它是某种本地 DNS 服务器,或者可能是代理或其他东西,但我所知道的是它首先尝试路由器的 DNS 服务器,而这正是我所不知道的不想发生。

关于 use-vc 选项,出于某种原因,当我阅读有关详细信息时,我看到了 TCP 并阅读了 TLS,这解释了为什么它不起作用。TCP 和 TLS 不是同一件事,但是当您研究某件事足够长的时间但没有结果时,像这样的小区别就会开始变得模糊!

mpb*_*den 6

~.通过将全局域路由添加到配置文件,您似乎已经找到了问题的解决方案/etc/systemd/resolved.conf。虽然这似乎有效,但我认为这种方法有局限性,因为您只能影响此文件中全局 DNS 设置的更改,而不能影响特定链接。但有一种方法可以通过 D-Bus 接口绕过这个障碍。

D-Bus 是一种进程间通信系统,允许将消息发送到各种服务。在这种情况下,您希望与systemd-resolved通信以更改单个链接上的 DNS 设置,而不是仅全局更改该/etc/systemd/resolved.conf文件。与 D-Bus 通信的方法有多种,从低级 D-Bus API 到高级绑定。但这可能会变得有点复杂,因此我们将利用 openvpn 设置的帮助程序脚本来使事情尽可能简单。在此脚本中,命令 ,busctl用于与 D-Bus 通信并更改各个链路上的设置。此时,信息性多于有用性,因为您不会busctl直接与之交互。但这并不是说您将来不能或不会。

可以说,全局域路由~.是控制 DNS 查询默认路由的关键。它基本上表示,对任何其他链接上未明确列出的所有域的 DNS 查询将被路由到与全局域路由设置的链接关联的 DNS 服务器。这有点拗口,但希望随着我们的继续,这会更有意义。

我还想提一下,编辑单个链接而不是定义全局 DNS 设置的一个优点是,它将在未来为诸如分割 DNS 之类的情况提供更大的灵活性。我不会在这里详细介绍,但可以在以下链接中找到拆分 DNS 以及路由域和搜索域的说明:

不管怎样,我觉得我在下面概述的方法可能比我之前的答案更符合systemd-resolved的预期用途。因此,这是我的首选答案,也是我配置系统的方式。

让我们开始吧...


设置/etc/resolv.conf

确保/etc/resolv.conf是 的符号链接/run/systemd/resolve/stub-resolv.conf。这是 Ubuntu 22.04 中的默认设置。即使使用 127.0.0.53 处的本地解析器,它仍然会查询任何上游 DNS 服务器以获取不在其缓存中的记录。它应该如下所示:

$ ls -l /etc/resolv.conf
lrwxrwxrwx 1 root root 39 Apr 22 23:47 /etc/resolv.conf -> ../run/systemd/resolve/stub-resolv.conf
Run Code Online (Sandbox Code Playgroud)

如果/etc/resolv.conf是静态文件或除 之外的任何文件的符号链接 /run/systemd/resolve/stub-resolv.conf,请执行以下操作:

$ sudo rm /etc/resolv.conf
$ sudo ln -s /run/systemd/resolve/stub-resolv.conf /etc/resolv.conf
Run Code Online (Sandbox Code Playgroud)

设置/etc/systemd/resolved.conf

接下来,不要在 中设置任何全局 DNS 服务器/etc/systemd/resolved.conf。稍后我们将在其他地方为您的 VPN 隧道设置特定的 DNS 服务器。

关于DNS-over-TLS的设置,您可以全局设置它们,也可以针对任何特定链接进行设置。对于此设置,我们将全局设置它们,/etc/systemd/resolved.conf以便它影响所有链接上的所有服务器。另外,我们将其设置opportunistic为而不是这样yes,无论服务器是否支持DNS-over-TLS,它都会成功。如果您想要强制执行DNS-over-TLS,但如果不支持则失败,请将其更改为yes.

编辑/etc/systemd/resolved.conf如下:

#DNS=
#FallbackDNS=
#Domains=
#DNSSEC=no
DNSOverTLS=opportunistic
#MulticastDNS=no
#LLMNR=no
#Cache=no-negative
#CacheFromLocalhost=no
#DNSStubListener=yes
#DNSStubListenerExtra=
#ReadEtcHosts=yes
#ResolveUnicastSingleLabel=no  

                       
Run Code Online (Sandbox Code Playgroud)

设置开放VPN

使用 openvpn,通常会将 DNS 服务器推送到客户端,但系统不会自动配置为使用它们。这是因为该过程在不同操作系统上略有不同。对于 Ubuntu,openvpn 安装通常会提供一个帮助程序脚本来实现 DNS 服务器通过隧道重定向查询,从而防止 DNS 泄漏。这是我在开始时提到的我们将要使用的帮助程序脚本。

以下是.ovpn文件的当前配置:

client
dev tun
proto udp
remote (redacted)
nobind
remote-cert-tls server
tls-version-min 1.2
verify-x509-name (redacted)
cipher AES-256-CBC
auth SHA256
auth-nocache
askpass (redacted)
verb 3
script-security 2
up /etc/openvpn/update-resolv-conf
down /etc/openvpn/update-resolv-conf
redirect-gateway
dhcp-option DNS 10.12.216.2
Run Code Online (Sandbox Code Playgroud)

查看.ovpn文件中的以下行:

script-security 2
up /etc/openvpn/update-resolv-conf
down /etc/openvpn/update-resolv-conf
Run Code Online (Sandbox Code Playgroud)

本节定义了一个名为 的帮助程序脚本update-resolv-conf,该脚本在 VPN 运行时运行updown。在脚本中,应用程序调用/sbin/resolvconf. 但在 Ubuntu 22.04 的默认安装中这会失败,因为/sbin/resolvconf没有安装。这是因为您的系统正在运行systemd并使用systemd-resolved

/sbin/resolvconf是一个用于/etc/resolv.conf直接管理和修改配置文件的框架,但该文件不再像以前那样使用。(正如您现在所知,它是 的符号链接/run/systemd/resolve/stub-resolv.conf。)不用说,您需要一个名为 的不同帮助程序脚本,update-systemd-resolved它用于busctl访问 D-Bus 接口。

使用以下命令安装帮助程序脚本:

sudo apt install openvpn-systemd-resolved
Run Code Online (Sandbox Code Playgroud)

这会将脚本放入/etc/openvpn.

安装此脚本后,修改.ovpn文件并将前面的行替换为以下内容:

script-security 2
up /etc/openvpn/update-systemd-resolved
down /etc/openvpn/update-systemd-resolved
down-pre
dhcp-option DOMAIN-ROUTE .
Run Code Online (Sandbox Code Playgroud)

您需要重新启动系统才能生效,但首先查看当前的输出resolvectl

在对.ovpn进行任何更改之前:

$ resolvectl
Global
       Protocols: -LLMNR -mDNS +DNSOverTLS DNSSEC=no/unsupported
resolv.conf mode: stub
     DNS Servers:

Link 2 (enp0s10)
Current Scopes: DNS
     Protocols: +DefaultRoute +LLMNR -mDNS +DNSOverTLS DNSSEC=no/unsupported
   DNS Servers: 192.168.1.1
    DNS Domain: localdomain

Link 3 (tun0)
Current Scopes: none
     Protocols: -DefaultRoute +LLMNR -mDNS +DNSOverTLS DNSSEC=no/unsupported

Link 4 (wlp5s0)
Current Scopes: none
     Protocols: -DefaultRoute +LLMNR -mDNS +DNSOverTLS DNSSEC=no/unsupported
Run Code Online (Sandbox Code Playgroud)

重新启动系统并resolvectl再次运行。请注意以下更改:

  • 将 .ovpn 文件添加dhcp-option DOMAIN-ROUTE ..ovpn文件后,全局路由域~.已添加到 .ovpn文件中Link 3 (tun0)
  • 协议-DefaultRoute已更改为+DefaultRouteon Link 3 (tun0)
  • 10.12.216.2已添加DNS 服务器Link 3 (tun0)。这是因为您的.ovpndhcp-option DNS 10.12.216.2文件中包含了该行。(还可能列出Link 3 (tun0)从 VPN 服务器推送给您的其他 DNS 服务器。)

更改为.ovpn后:

$ resolvectl
Global
       Protocols: -LLMNR -mDNS +DNSOverTLS DNSSEC=no/unsupported
resolv.conf mode: stub
     DNS Servers:

Link 2 (enp0s10)
Current Scopes: DNS
     Protocols: +DefaultRoute +LLMNR -mDNS +DNSOverTLS DNSSEC=no/unsupported
   DNS Servers: 192.168.1.1
    DNS Domain: localdomain

Link 3 (tun0)
Current Scopes: none
     Protocols: +DefaultRoute +LLMNR -mDNS +DNSOverTLS DNSSEC=no/unsupported
Current DNS Server: 10.12.216.2
       DNS Servers: 10.12.216.2
        DNS Domain: ~. 
        
Link 4 (wlp5s0)
Current Scopes: none
     Protocols: -DefaultRoute +LLMNR -mDNS +DNSOverTLS DNSSEC=no/unsupported    
Run Code Online (Sandbox Code Playgroud)

此时,隧道将用作10.12.216.2所有域查询的默认 DNS 服务器。但是,如果您想使用最初列出的 DNS 服务器(并且仅使用它们)9.9.9.9149.112.112.112,那么您需要执行以下操作:

  • 忽略从 VPN 服务器推送给您的任何 DNS 服务器。
  • 在.ovpn文件中具体列出您要使用的DNS 服务器。
  • 省略.ovpn文件中列出的当前 DNS 服务器

所有这些都可以通过将以下内容添加到.ovpn文件中来完成:

pull-filter ignore "dhcp-option DNS"
dhcp-option DNS 9.9.9.9
dhcp-option DNS 149.112.112.112
Run Code Online (Sandbox Code Playgroud)

并且不要忘记删除或注释掉.ovpndhcp-option DNS 10.12.216.2文件中当前的内容。

更改后,您的.ovpn文件应如下所示:

client
dev tun
proto udp
remote (redacted)
nobind
remote-cert-tls server
tls-version-min 1.2
verify-x509-name (redacted)
cipher AES-256-CBC
auth SHA256
auth-nocache
askpass (redacted)
verb 3
script-security 2
up /etc/openvpn/update-systemd-resolved
down /etc/openvpn/update-systemd-resolved
down-pre
dhcp-option DOMAIN-ROUTE .
redirect-gateway
pull-filter ignore "dhcp-option DNS"
dhcp-option DNS 9.9.9.9
dhcp-option DNS 149.112.112.112
Run Code Online (Sandbox Code Playgroud)

重新启动并运行resolvectl。您的 DNS 服务器已添加到Link 3 (tun0). 之前在.ovpn文件中列出的或从 VPN 服务器推送给您的任何 DNS 服务器现在都消失了。

$ resolvectl
Global
       Protocols: -LLMNR -mDNS +DNSOverTLS DNSSEC=no/unsupported
resolv.conf mode: stub
     DNS Servers:

Link 2 (enp0s10)
Current Scopes: DNS
     Protocols: +DefaultRoute +LLMNR -mDNS +DNSOverTLS DNSSEC=no/unsupported
   DNS Servers: 192.168.1.1
    DNS Domain: localdomain

Link 3 (tun0)
Current Scopes: none
     Protocols: +DefaultRoute +LLMNR -mDNS +DNSOverTLS DNSSEC=no/unsupported
Current DNS Server: 9.9.9.9
       DNS Servers: 9.9.9.9 149.112.112.112
        DNS Domain: ~.
        
Link 4 (wlp5s0)
Current Scopes: none
     Protocols: -DefaultRoute +LLMNR -mDNS +DNSOverTLS DNSSEC=no/unsupported
Run Code Online (Sandbox Code Playgroud)

结论

通过像这样的系统设置,该全局路由域~.设置为Link 3 (tun0)状态,未在不同链接上专门列出的域的所有 DNS 查询都将路由到 列出的 DNS 服务器Link 3 (tun0)。例如,如果您~example.com在 上设置为路由域Link 2 (enp0s10),则对以 结尾的域的任何 DNS 查询都example.com将路由到位于 的本地 DNS 服务器192.168.1.1。所有其他服务器将被发送到 列出的 DNS 服务器Link 3 (tun0)

正如您所看到的,这是高度可定制的。如果将来您还想配置另一个 VPN(公司或私人),并使用通过该路由的特定 DNS 查询,那么您可以这样做。


额外的

最后一点,我之前提到过,安装的帮助程序脚本通过 D-Bus 对系统进行更改,特别是使用命令busctl。请随意检查脚本并查看此处的手册busctl

然而,一个更容易使用的接口以及您熟悉的命令是resolvectl,它是 D-Bus 接口的一个薄包装。查看以下示例:

设置全局路由域tun0

sudo resolvectl domain tun0 ~.
Run Code Online (Sandbox Code Playgroud)

清除以下所有域tun0

sudo resolvectl domain tun0 ""
Run Code Online (Sandbox Code Playgroud)

设置tun0为默认路由:

sudo resolvectl default-route tun0 yes
Run Code Online (Sandbox Code Playgroud)

定义 DNS 服务器tun0

sudo resolvectl dns tun0 9.9.9.9
Run Code Online (Sandbox Code Playgroud)

清除 DNS 服务器tun0

sudo resolvectl dns tun0 ""
Run Code Online (Sandbox Code Playgroud)

设置 DNS-over-TLS 为tun0

sudo resolvectl dnsovertls tun0 yes
Run Code Online (Sandbox Code Playgroud)

通过这些命令以及更多未显示的命令,您可以更改各个链接的设置。最酷的是,这种变化是立竿见影的。无需重新启动或重新启动服务,因此这是一种有趣的玩耍和实验方式。不幸的是,这些更改在重新启动后将无法保留,因此上面使用的帮助程序脚本是有益的。没有它,您就必须自己编写。

要了解有关使用更改每个链接的 DNS 设置的更多信息,请查看此处的resolvectl手册页。同时,这是相关部分:

dns [LINK [SERVER...]], domain [LINK [DOMAIN...]], default-route
[LINK [BOOL...]], llmnr [LINK [MODE]], mdns [LINK [MODE]], dnssec
[LINK [MODE]], dnsovertls [LINK [MODE]], nta [LINK [DOMAIN...]]
    Get/set per-interface DNS configuration. These commands may
    be used to configure various DNS settings for network
    interfaces. These commands may be used to inform
    systemd-resolved or systemd-networkd about per-interface DNS
    configuration determined through external means. The dns
    command expects IPv4 or IPv6 address specifications of DNS
    servers to use. Each address can optionally take a port
    number separated with ":", a network interface name or index
    separated with "%", and a Server Name Indication (SNI)
    separated with "#". When IPv6 address is specified with a
    port number, then the address must be in the square brackets.
    That is, the acceptable full formats are
    "111.222.333.444:9953%ifname#example.com" for IPv4 and
    "[1111:2222::3333]:9953%ifname#example.com" for IPv6. The
    domain command expects valid DNS domains, possibly prefixed
    with "~", and configures a per-interface search or route-only
    domain. The default-route command expects a boolean
    parameter, and configures whether the link may be used as
    default route for DNS lookups, i.e. if it is suitable for
    lookups on domains no other link explicitly is configured
    for. The llmnr, mdns, dnssec and dnsovertls commands may be
    used to configure the per-interface LLMNR, MulticastDNS,
    DNSSEC and DNSOverTLS settings. Finally, nta command may be
    used to configure additional per-interface DNSSEC NTA
    domains.

    Commands dns, domain and nta can take a single empty string
    argument to clear their respective value lists.

    For details about these settings, their possible values and
    their effect, see the corresponding settings in
    systemd.network(5).
Run Code Online (Sandbox Code Playgroud)