Linux VPN 的回复消失了?检查 rp_filter 与路由
IPv4 源验证可能会丢弃反向路由与模式不匹配的数据包。在关闭它之前,请先检查路由和实际生效的设置。
请求离开了主机,VPN 对端也作出了回应,可回复始终没有到达套接字。隧道是通的,对端日志显示数据包已经发出,而应用层没有任何东西能解释这份沉默。在 Linux 上,常见的嫌疑之一是反向路径过滤:当源验证与内核为该来源预期的路由不一致时,进入的 IPv4 数据包可能被丢弃。按顺序排查能告诉你这是原因还是巧合。它还能避免你为了修一条缺失的路由而关掉一项安全控制。
源验证在判断什么
内核会去路由表里查一条回到该来源的路由,以判断数据包的源地址对它所到达的接口是否合理。如果反向路由指向别处,数据包可能在任何防火墙规则或应用套接字看到它之前就被丢弃。这项检查的存在是为了让某些伪造模式更难成功,也正是为什么不对称和基于策略路由的设计会撞上它,而同一网络里的普通客户端一切正常。普通客户端拥有对称路由,而这正是该检查所假定的前提。
关键词是合理。数据包可能完全合法,却仍然通不过检查,因为被检验的并不是合法性。
读取实际生效的模式,而不是单个字段
共有三种模式:禁用、严格和宽松。严格模式对路由的预期与宽松不同,而全局设置又会与各接口的设置相互作用,所以只读一个文件可能错误地描述实际生效的状态。两者都要读,而且要在接收回复的那个接口上读,而不是在默认路由的接口上读。
sysctl net.ipv4.conf.all.rp_filter
sysctl net.ipv4.conf.<iface>.rp_filter
ip route get <source-of-the-reply>第三行里的路由查询正是人们常跳过的那一步。它告诉你内核对来源的判断,而这恰恰就是该检查的输入。
先找到不匹配,再改动检查
弄清楚回复为何从那个接口进来,以及它的反向路由是否本就该不同。让请求走一条路径、而回复从另一条路径返回的策略规则是一种合法设计,而这正是严格验证所拒绝的设计。策略路由中使用的标记在这里也可能起作用,因为它们会改变反向查询返回的结果。
如果反向路由只是缺失,补上它就是修好了原因,而不是关掉一项检查。缺失的路由、防火墙的拒绝和错误的流量选择器会呈现出相同的可见症状,而其中只有一项与源验证有关。
不要把全局关闭源检查当作第一反应。那等于用一个具体的诊断换一次宽泛的行为改变,而后者你以后还得记住。
一种你能为之辩护的选择性调整
如果确实需要一处窄范围的调整,就把它限制在需要它的接口上,写下原因,并放进拥有该接口的配置里,好让它在重启后依然存在。然后重新测试当初失败的那条确切流程,从同一来源到同一目的地,而不是对某个顺手的目标做一次 ping。
要包含一个仍应被拒绝的用例。如果那个用例现在通过了,说明这次调整削弱的不止是你要修的那一条路径。
把 IPv4 和 IPv6 分开看
看起来眼熟的 sysctl 并不是针对每个地址族的策略。IPv6 有自己的行为和自己的设置,一台机器完全可能在一个地址族上严格、在另一个上宽松,直到某个具体目的地失败才有人察觉。如果工作负载两者都用,就两者都检查。
把模式、接口、路由查询的输出以及改动的原因记录在同一处。下一个人会找到这个设置却找不到任何解释,然后要么删掉它,要么把它抄到本不该在的地方。