测试页面上出现了一个 IPv6 网段的地址,隧道显然没有承载它,而网上最流行的建议是把 IPv6 到处关掉。那条命令确实能让面板安静下来。但它同时移除了接入网络可能依赖的协议,也让你无从判断这个地址究竟是真正的泄漏、链路本地接口地址,还是解析器在正常工作。决定正确做法的是三个条件,而只有其中一个通向阻断。

先搞清楚工具到底测到了什么

最常见的误报是链路本地地址,它以 fe80: 开头,存在于每一个支持 IPv6 的接口上,无论这台机器能否通过 IPv6 访问互联网。它会出现在接口列表和某些应用诊断里,与你的流量路径毫无关系。

第二种误报是 fc00::/7 范围内的唯一本地地址,这是网络内部使用的私有网段。把所有主机地址都打印出来的工具会乐于把它列在公网地址旁边。读得快的人会把它叫作泄漏,然后在一个并不存在的问题上花掉一小时。

第三种根本不是误报。能在隧道之外到达互联网的全局地址才是真正的泄漏,值得在做出任何决定之前先测一测:

ip -6 addr show scope global
ip -6 route show default
curl -6 -sS --max-time 6 https://ifconfig.co

如果第一条命令打印出全局地址,而第三条返回了一个不属于隧道的公网 IPv6 地址,那么你确实有一条绕开隧道的路径。如果第一条只打印出链路本地条目,那就从来没有需要修的泄漏。

三个条件决定该用哪种控制

决定不是「IPv6 好」或「IPv6 坏」,而是你处在三种情况中的哪一种,每种都有不同的正确做法,做错时的代价也不同。

SituationCorrect controlCost if you disable IPv6 instead
Exit has no IPv6, host doesBlock IPv6 deliberately in the tunnel policySame visible result, no documentation of intent
Exit carries IPv6, host leaksRoute IPv6 into the tunnel and verifyYou lose IPv6 reachability entirely
Access network is IPv6-onlyChange nothing; use the tunnel's IPv6Machine loses connectivity completely

第一行是人们通常所指的情况,也是合理的。出口不支持 IPv6 的客户端不该悄悄放主机自带的 IPv6 流量出去,因为那正是有据可查的双栈泄漏。要点在于把它作为明示的策略来阻断,而不是在内核里关掉一个协议。两者可见的结果相同,而只有一种日后还能被审查。

第三行才是一刀切建议真正有害的地方。移动运营商和部分家庭宽带运行的是 IPv6 单栈网络,对 IPv4 目的地做转换。在那里去掉 IPv6,就等于去掉网络提供的唯一传输方式,造成的故障比你正在排查的那个严重得多。

如果出口没有 IPv6,就有意地阻断它

优先用客户端自身的机制,因为它在隧道建立期间才生效,隧道断开时自动撤销。OpenVPN为此提供了选项,其他客户端则可以用 iptables 的等价规则:

block-ipv6
ip6tables -A OUTPUT -o eth0 -j REJECT --reject-with ipv6-port-unreachable

指令应该写在配置文件里,规则应该写在你能审查和删除的防火墙里。两者都是失败即关闭,这正是你要的,而且两者都能在配置差异里看到;埋在启动脚本里的内核 sysctl 做不到这一点。

记下它为什么在那儿。没有注释的阻断规则会被下一个需要 IPv6 做别的事的人删掉,然后泄漏就悄悄回来了。

如果出口承载 IPv6,就为它做路由,并把两个协议族都测一遍

服务器支持时,承载 IPv6 是更好的答案,而且难度并不高多少。要求是隧道接口在该协议族里有一个地址,并且它的默认路由指向隧道内部,而不是走物理接口出去:

ip -6 route get 2606:4700:4700::1111
ip -6 route show default
curl -6 -sS --max-time 6 https://ifconfig.co
curl -4 -sS --max-time 6 https://ifconfig.co

路由查询才是决定性的测试:如果它点名了隧道接口,说明 IPv6 流量正在被承载。随后两次 curl 调用确认两个协议族各自看到什么,把它们放在一起比较,才能证明两条路径现在走的是同一个出口。

别停在连接成功上。重连隧道再查一次,因为服务器只在初始配置里下发时,推送的 IPv6 路由可能在重新建立后丢失。接着再查一个被排除的目的地,因为分流隧道的规则正是两个协议族最容易不一致的地方。

如果接入网络只有 IPv6,就不要动它

有些网络只提供 IPv6,并指望设备通过运营商侧的转换机制访问 IPv4 服务。在这种机器上关掉 IPv6 不是安全改进,而是砍掉了通往任何地方的唯一路径。这是为了一个谁都不需要的测试结果而自找的故障。

这里做错的症状很特别也很难受:隧道看起来建立了,域名解析也正常,但什么都加载不出来。如果改动 IPv6 之后出现这种情况,立刻恢复设置,换个地方继续排查。

无论你选哪一种,都要把决定明确写进配置文件或防火墙,把它绑在出口真正支持的能力上,并记下RFC 7359 关于双栈的讨论所描述的双栈行为。明示的阻断是一种策略。被关掉的协议是一种没人能审计的绕行办法,而从测试页面看,两者一模一样。

相关文章