在 OpenVPN 的 SNAT 规则拖垮 nginx 之前收窄它
客户端能通过认证却什么都路由不到,或者 VPN 一起来服务器自己的网站就不再响应。先确认 tun 是否可用,再把地址转换限定在地址池和单个网卡上。
同一类主机上反复出现两种故障。第一种是客户端认证成功之后,隧道之外什么都到不了。第二种是 VPN 起来之后,服务器自己的网站停止响应,这种更让人警觉,因为它看起来像被殃及。在基于容器的 VPS 上,这两者通常都能归结到同一份简短的检查清单,而且顺序很重要:先看设备,再看转换,最后看范围。
确认容器能否创建 tun 设备
如果隧道接口根本创建不出来,别的都不用谈。检查 /dev/net/tun 是否存在,以及能否成功创建测试设备,因为很多服务商默认在容器里禁用 tun。防火墙做得再多,也补不上一台从未创建出来的设备。
如果设备缺失,只有主机方或服务商才能启用它。换个套餐往往比开一个拖上一周的工单更便宜。在读懂任何一行 OpenVPN 日志之前先确认这一点。否则你会花一整晚去调那些永远看不到数据包的规则。
确定 MASQUERADE 在这个网卡上到底做什么
在普通虚拟机上,常见的出站规则是 POSTROUTING MASQUERADE 从主网卡出去。在 OpenVZ 容器里,能看到的设备往往是 venet0,它是主机托管的虚拟接口而不是被路由的网卡,MASQUERADE 在那里的行为并不总是教程假设的样子。
可行的做法是把转换明确指向客户端本来就能到达的地址,并且两个方向都允许转发。从服务器配置里替换地址池,和替换地址同样重要。把每一行读一遍再粘贴。
iptables -t nat -A POSTROUTING -o venet0 -s 10.8.0.0/24 -j SNAT --to-source 203.0.113.10
iptables -A FORWARD -i tun0 -j ACCEPT
iptables -A FORWARD -o tun0 -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT没有转发规则,转换照样发生,内核照样拒绝转发。这两件事凑在一起,就会出现计数在涨、客户端却什么都收不到的怪状况。那两行 FORWARD 也必须排在同一个链里任何宽泛的丢弃规则之前。顺序是配置的一部分,不是实现细节。
把规则收窄到一个地址池和一个网卡
第二种症状另有原因:一条既没有源限制也没有出站限制的 POSTROUTING 规则。这样的规则会改写主机发出的每一个数据包。
依赖原始源地址的服务随后就会出错。本地代理后面的 Web 服务器可能不再响应,而重启它毫无用处,因为规则还在路径上。主机发往自身公网地址的流量同样受影响,所以故障看起来像 DNS 或证书问题,而不像防火墙问题。这条规则几乎肯定是为了修第一个症状才加上的。
正确的做法是把匹配收窄到隧道地址池和出站网卡,然后按地址池各加一条规则,而不是把一条规则放宽去覆盖多个地址池。
让规则持久化,并查清还有谁在管它们
手工添加的规则重启就没了,所以要用 iptables-save 保存,或者在装了 netfilter-persistent 的地方用它来管理。确认恢复脚本在 OpenVPN 启动之前执行。然后主动重启一次,而不是等出事时才发现缺口。
在容器主机上,还要确认持久化文件位于可写的容器存储里,并且服务商不会在启动时替换过滤规则。有些平台在容器之外管理策略,那里的本地规则要么被忽略,要么和主机自己的规则互相打架。
用计数器和一个抓包流来验证
从已连接的客户端看外部服务报告的公网地址。如果它和服务器地址一致,说明转换生效;如果不一致,数据包要么走了别的路径,要么根本没出去。接着分别测试服务器上托管的服务和公网上的服务,因为它们经过同一套规则走的是不同的路径。
在服务器上用 iptables -t nat -L -v -n 读取 NAT 和 FORWARD 规则上的包计数,并在抓包里观察一条流。如果抓包里出现了客户端地址却迟迟没有回包,缺口就在回程路径或服务商的过滤上,而不在客户端配置里。把抓到的源地址和规则转换后的地址对比一下,不一致说明是另一条规则先匹配上了。观察一条流,比读一页配置更能定论。
把地址池、转换后的地址和网卡名一起写进变更记录。对某个地址池正确的规则,换个地址池就会转换错。