Pi 热点搭配 Pi-hole 与 VPN:为每项服务分配一个角色
在同一个 Pi 上启用三个帮手之前,先确定 DHCP、DNS 和包转发各归谁管。重复的属主会带来互相竞争的租约和解析器环路。
手机连上 Pi 的热点,拿到了地址,页面却仍然打不开。Pi 上跑着热点帮手、Pi-hole 和 VPN 客户端,每个都认为自己拥有路径的一部分。这类搭建失败,多半不是因为某个组件坏了,而是因为两个组件在做同一件事。调整其中任何一个之前,先为每个功能指定一个属主。
为每个功能指定一个属主
这里有四个功能,每个都需要且只需要一个属主。下表列出功能、可行的属主,以及两个服务争抢时的故障表现。
| Function | Workable owner | Duplicate-owner symptom |
|---|---|---|
| Client addresses | One DHCP server, such as the hotspot helper | Competing offers and changing leases |
| DNS reception | Pi-hole on port 53 | Bind failure or queries answered by the wrong process |
| Forwarding and NAT | One firewall manager | Double translation and broken return paths |
| VPN routing | One client configuration | Route flapping and traffic escaping outside the tunnel |
重叠通常是一步步装出来的。热点帮手自带一份 dnsmasq,Pi-hole 要占用 53 端口,VPN 客户端又添加路由。每一步单看都合理,合起来就得到一台机器:后启动的服务悄悄改变了先启动服务的行为。
在启用其他东西之前,把属主写下来。搭建笔记里放一张小表,就能避免后来的安装接管已经有人负责的功能。
选一个网段,并让它远离隧道
选定热点网段、Pi 在该网段的地址、DHCP 地址池,以及客户端获得的解析器。让这个网段与上游 LAN 和 VPN 隧道网段都不重叠,因为重叠的子网会把路由问题变成地址转换工程。与隧道重叠的网段会让 Pi 变成两个相同前缀之间的意外路由器。把网段和地址池写进热点配置,而不是留给默认值。
如果热点由 NetworkManager 的共享模式管理,在添加 DHCP 服务器之前先检查实际生效的连接。共享模式会启动自己的帮手并开启转发,所以它已经是该接口地址规划的属主。仅仅因为可用就再加一个服务器,得到的是互相竞争的分配,而不是冗余。
IPv6 要有意识地决定。许多配置里,路由通告和 DHCPv6 来自与 DHCPv4 不同的帮手;在 VPN 策略只覆盖 IPv4 时继续开着 IPv6,会给客户端一条绕过隧道的路。要么规划 IPv6 一侧,要么在热点接口上关掉它,并写下理由。
53 端口上到底是谁在应答
改配置之前,先列出所有监听 53 端口的进程。在同时跑本地解析器存根、Pi-hole 和共享帮手的 Pi 上,监听列表是判断谁在应答查询的唯一可靠地图:
sudo ss -lunpt '( sport = :53 )'
ip -4 addr show
ip routePi-hole 的接口指南说明,只限定一个接口的接收模式仍然可能使用通配套接字,并丢弃从别处到达的请求。这与只绑定选定接口不是一回事,而当另一个 DNS 服务必须共用这台机器时,后者才是本来要用的安排。
两个进程通常无法绑定同一个地址和端口,尽管 SO_REUSEPORT 可以让共享绑定成功;即便如此,通配监听者也可能截走本应交给更窄服务的请求。为想要的角色选择文档写明的模式,并按预期顺序重启服务。
把 Pi-hole 的管理页面放在热点网络之外。客户端需要的是解析器,而不是能改动解析器配置的网页界面或 API。
两段 DNS,各自一套策略
客户端查询从手机走到 Pi,另一次查询从 Pi 走到它的上游解析器。匹配转发过来的热点流量的路由规则,不会自动作用于第二段,因为这部分流量是 Pi 在本地生成的。如果 VPN 应当承载 DNS,本地路径就需要自己的策略。
当心环路。把 Pi-hole 的上游指向 Pi 自己提供的地址,无论直接还是通过端口转发,都会产生一直转到超时的查询。给它一个真实的上游,或为它答不了的域名设置条件转发。
决定隧道断开时会发生什么。回落到本地解析器能让浏览继续,但可能把域名泄漏到 VPN 之外;阻断则保持策略完整,把故障变成看得见的错误。两种选择都站得住,但必须主动选择,而不是继承默认值。
验证路径和断线时的行为
分层测试:客户端拿到地址,客户端通过 Pi 解析域名,Pi 通过预期的上游解析,应用完成一次请求。ping 通说明不了 DNS,解析成功也说明不了转发。
dig @192.168.70.1 example.com
curl -sS -o /dev/null -w '%{http_code}\n' https://example.com/
ip -4 route get 1.1.1.1然后停掉隧道再重复一遍。预期行为应在几秒内出现,而不是手动重启 Pi-hole 或热点服务之后。故障期间检查客户端的解析器设置,因为备用公共解析器在策略期待失败时仍会照常应答。
转发和 NAT 只留一个属主,并从客户端验证,而不是从 Pi 本机验证。成功离开 Pi 的请求仍可能在回程被丢弃,而客户端是唯一能看到整条路径的地方。