客户端完成认证,从地址池拿到地址,也能访问 VPN 主机上的服务,但任何公网目标都会超时。这就是一条没有出口的隧道的样子,而且只要四件事没同时成立,它就一直是这样。客户端必须接受默认路由,服务器必须转发,防火墙必须放行转发流量,而回包需要一条回到地址池的路径。四种故障都表现为同一个症状,所以每次只改一项,并记录结果。

下发默认路由,并确认客户端到底装上了什么

服务器端那一行很短,而且 它只影响接受推送选项的客户端。def1 形式会安装两条半默认路由,而不是删掉原有默认路由,这样既保住当前连接,又仍能捕获所有目标。

push "redirect-gateway def1"

重新连接后,要读客户端自己的路由表,而不是相信配置文件。在 Linux 上,两条 /1 条目会出现在原默认路由旁边;Windows 则把同一对显示为 0.0.0.0/1 和 128.0.0.0/1。忽略推送选项、或被配置为拒绝这些选项的客户端会保留旧默认路由,永远不会把上网流量送进隧道。在改动服务器之前先把这个问题定下来,因为服务器端任何规则都补不回来。

转发是服务器的设置,不是 VPN 的设置

动路由之前先读转发标志。那里为零意味着内核收到客户端的数据包却拒绝转出去,从远端看就像被防火墙拦下一样。通过系统的配置目录设置这个值,让它在重启后仍然生效。

sysctl net.ipv4.ip_forward
echo 'net.ipv4.ip_forward=1' | sudo tee /etc/sysctl.d/99-vpn-forward.conf
sudo sysctl --system

接着确认主机本身有一条可用的、通往互联网出口的路由。从服务器命令行 发出的请求只能证明上行链路可用,对转发客户端数据包毫无说明,所以要把两者当作两次独立的测试。如果服务器根本到不了目的地,再多转发配置也无济于事。

只放行你真正需要的转发,别的都不放

被转发的数据包走 forward 路径;发往 VPN 主机自身的流量走 input 路径。这是两个不同的判断,打开 input 路径并不等于允许穿过这台机器。把两者混为一谈,就是防火墙看起来什么都放行、客户端却仍然超时的最常见原因。

最小策略是:允许地址池向出口接口发起新连接,并允许属于已建立流的回包。把它写进你的发行版所用的规则集归属处,而不是每次启动都追加一条重复规则;重复规则会越积越多,计数器也就失去意义。

sudo iptables -A FORWARD -i tun0 -o eth0 -s 10.90.0.0/24 -j ACCEPT
sudo iptables -A FORWARD -i eth0 -o tun0 -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT

把所有链都改成放行能让测试通过,却抹掉了防火墙存在的理由。把范围限定在地址池和出口接口上,与它们无关的转发不要动。

给回包留一条去处

地址池里的地址是私有地址,公网服务器无法直接把回包送到它们手上。要么由 VPN 主机在出方向做源地址转换,要么由上游设备保留一条回到地址池的路由。

sudo iptables -t nat -A POSTROUTING -s 10.90.0.0/24 -o eth0 -j MASQUERADE

只加一层地址转换。再往上游加第二层 NAT 会掩盖第一层,日后出问题时两边的日志几乎都没用。如果上游路由器已经把地址池回程路由做好了,在主机上再做转换就是多余的,回程就取决于那台路由器的配置,而不是你的配置。

云上的实例还会多一道关卡:有些平台会丢弃源地址不属于该实例的数据包,除非放宽这项检查。这个设置位于来宾之外,本地测试机正常时很容易被忘掉。

要从客户端证明,而不是从服务器命令行 证明

先用数字形式的 IPv4 请求,再做名字解析,最后再试应用。ping 只能证明转发和地址转换,它对名字解析什么也证明不了,因为 DNS 选项要等客户端侧集成把它装上才会生效。

curl -4 -m 5 http://198.51.100.10/
dig +short example.com

边做边记下改动和回滚步骤,这样测试失败时可以用一个动作把出口策略撤回去。这套做法只覆盖 IPv4;在声称部署承载全部流量之前,请单独清点 IPv6。

相关文章