把 OpenVPN 客户端指向另一台主机上的 Pi-hole 解析器
推送的 DNS 选项只是指定了一台服务器。VPN 网关需要转发,Pi-hole 需要回程路径和正确的监听模式,而客户端必须真正使用它。
把 Pi-hole 地址写进 OpenVPN 配置文件,既不会建立通往它的路径,也不会授予查询权限,更不保证客户端会使用它。当 Pi-hole 位于另一台 LAN 机器上时,有四件事必须对齐:来自客户端的路由、网关上的转发、回到地址池的回程路径,以及 Pi-hole 自身的接收策略。每种故障在日志里都长得不一样。按这个顺序处理,比先修最后一个更快。常见的捷径——对所有来源开放递归——是用一个小问题换一个永久问题。
推送的 DNS 选项做了什么、没做什么
服务器用下面的指令通告一个解析器,这里假定 192.168.50.53 是 Pi-hole 主机。客户端收到该选项后,会视其平台和解析器集成方式,把它应用到隧道接口上,或者弃之不用而改用本地存根。路由行和 DNS 行同样重要,因为客户端够不着的解析器根本不算解析器。这一切从服务器一侧都看不出来。
server 10.90.0.0 255.255.255.0
push "dhcp-option DNS 192.168.50.53"
push "route 192.168.50.0 255.255.255.0"
收到选项不等于使用它。带有解析器管理器的 Linux 客户端可能接受推送的服务器,但只把某些域名发过去;自己管理 DNS 的客户端则可能直接覆盖这项设置。推送的路由在某些平台上还受到限制,于是即便选项被接受,解析器依然不可达。要在客户端上查是哪个服务器在应答,而不是在服务器上查发了什么。
该选项对服务器一侧同样只字未提。在任何选项起作用之前,DNS 流量仍必须从客户端到网关、再从网关到 Pi-hole。缺失的路由和被拒绝的查询从客户端看是一样的,所以先确认网关上的转发。
从 Pi-hole 回到地址池的回程路径
如果保留客户端的源地址,Pi-hole 就会向 VPN 地址池内的地址回包。它自己的路由表多半对这个地址池一无所知,于是回包去了默认网关并消失。查询日志里会出现有请求、没有匹配应答的记录,这很容易被误读成防火墙问题。
给 LAN 路由器加一条到地址池的静态路由,下一跳指向 OpenVPN 网关;或者在转发给 Pi-hole 之前,在网关上做源地址转换。前者让每个客户端的地址在 Pi-hole 查询日志里保持可见。后者免去了回程路由的要求,但会让每个查询看起来都来自网关。两者都没错,它们回答的是不同的问题。
如果按客户端出报表或做策略很重要,静态路由是更划算的选择。如果 LAN 由别人管理、拿不到路由,源地址转换就是务实之选,代价是客户端身份丢失,这一点要写下来。记下你选了哪一种,因为之后每个日志问题都取决于它。
Pi-hole 针对被路由客户端的监听模式
DNS 下方设置页里的接口监听行为下拉框,决定 Pi-hole 应答哪些来源。最严格的选项只接受直连网段上的客户端;允许设备最多一跳之遥的选项,则把它扩展到相邻路由器后面的子网,而被路由的 VPN 客户端看起来正是如此。数据包确实到达了 LAN 接口,但它的源地址属于另一个子网。这是策略上的拒绝,而不是路由故障,读日志时这个区别很重要。
Pi-hole 的监听指南把本地直连的来源和被路由的来源区分开,到达接口并不是判断标准。选一个允许 VPN 地址池、但不允许整个互联网的模式,再配一条只接受来自该地址池 DNS 的防火墙规则。
为了让第一个查询成功而对所有来源开放递归,是错误的修法。它把私有解析器变成了开放解析器,正确的做法是收窄范围的授权,而不是全局授权。
把应答和查询日志对上
先问客户端,再查服务器,然后对比两者。在 systemd-resolved 客户端上,下面的命令会报告是哪个解析器给出了应答、每条链路配置了哪些服务器。用一个只有 Pi-hole 才知道的名字,因为直接查询 Pi-hole 地址只能证明可达,证明不了配置。
resolvectl query intranet.example.lan
resolvectl dns
预期的应答会把 192.168.50.53 列为服务器,并返回一个只有该解析器才知道的地址。然后再看 Pi-hole 自己的视图:pihole -t 会实时跟踪查询日志,每一行都带着源地址。如果来源是客户端的隧道地址,说明地址池路由完好。如果是网关的 LAN 地址,说明源地址转换在起作用,客户端自己的地址永远不会出现。
如果日志里什么都没出现,说明查询根本没到 Pi-hole,接下来要查的是网关上的转发。如果查询出现了却没有应答返回,问题就在回程路径,也就是上一节。这样拆分故障,就不会把路由错误当成防火墙规则。
检查网关的转发规则
网关必须把客户端的 DNS 包转发向 Pi-hole,而这条规则值得读一读,而不是想当然。下面的命令列出触及 53 端口的规则及其计数器,分别在 nftables 主机和 iptables 主机上执行。规则存在却显示零匹配,就是流量在转换之前被丢弃的信号。
sudo nft list ruleset | grep -A3 'dport 53'
sudo iptables -t nat -L POSTROUTING -n -v | grep 53
读计数器,而不只是看规则。一条从不匹配的规则,意味着包从别的接口出去了,或者客户端压根没发。一条在每个查询上都匹配的规则,意味着转发正常,问题在路径的更远处。
无论主机用哪种工具,检查都要保持狭窄:以地址池为源、以 53 端口为目的、以 Pi-hole 为唯一允许的目标。一条连 80 端口也放行的宽泛转发规则,是另一个迟早要发生的问题。