在 CGNAT 之后发布 VPN 客户端端口:检查清单
只有目的 NAT 规则只会得到半开连接。固定隧道地址、双向转换,并在测试前确认回程路径。
把服务放在运营商级 NAT 后的笔记本电脑上,让这台电脑经 VPN 指向 VPS,外面就没有人能够到达它。你在 VPS 上加了一条目的 NAT 规则,得到的却比沉默更糟:连接建立后随即卡住。回应并没有丢失,它正沿着客户端自己的默认路由离开,也就是说服务器从未看到它。请按顺序完成这五项检查,并在第一个不成立的地方停下。
确认服务器究竟会不会转发
在写下哪怕一条译文之前,先勾选这一项。对于内核拒绝转发的数据包,DNAT 规则会被规则解析器接受,随后又被转发路径丢弃,于是出现看起来像防火墙故障的超时。
sysctl net.ipv4.ip_forward
sysctl -w net.ipv4.ip_forward=1
在运行中的内核里设置它,与让它跨重启保留并不是一回事。把 net.ipv4.ip_forward=1 写入 /etc/sysctl.d/ 下的文件,执行 sysctl --system,然后回读该值,而不是相信文件已被读取。自行管理网络命名空间的容器,在该命名空间内部也需要同一个值,而不只是主机上。
如果回读仍然是零,就停在这里。后面的步骤都不会生效。
在任何规则引用隧道地址之前,先把它固定下来
地址池按连接顺序分配地址,所以为今天租约写的规则,下周可能悄悄指向另一台客户端。请把地址绑定到证书身份,而不是到达顺序。
client-config-dir /etc/openvpn/ccd
ifconfig-push 10.77.0.14 10.77.0.13
这一对是点对点隧道的客户端端与对端端,服务器以证书的通用名作为密钥。在子网拓扑下,第二个值会被当作网络掩码读取,所以要写出与所用拓扑相符的形式。然后重新连接客户端,并在客户端本身读取地址,因为服务器对地址池的看法并不能证明客户端配置了什么。
如果重新连接后地址发生变化,就停下。跟随移动目标的 NAT 规则会间歇性失败,什么也教不会你。
双向转换
目的转换把入站数据包送往客户端。源转换才让客户端回复服务器而不是原作者,而这正是人们常常漏掉的那条规则。
iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 29443 -j DNAT --to-destination 10.77.0.14:29443
iptables -t nat -A POSTROUTING -d 10.77.0.14 -p tcp --dport 29443 -j SNAT --to-source 10.77.0.13
没有第二条规则,客户端会经由自己的 ISP 上行链路回复,于是远端对端看到的源地址从未与它通信过,而服务器也没有为回应保留任何跟踪条目。外部观察者看到的是只完成一半的握手,而这正是让人去找并不存在的防火墙故障的症状。
用 iptables -t nat -S 回读这两条规则,并确认接口名就是承载公网地址的那一个。在已经用 nftables 过滤的主机上,请把转换写入相应地址族的 nftables NAT 链,而不是混用工具。
放行过滤表
NAT 表只改写头部,并不授予权限。在默认 DROP 策略下,被转发的流需要自己的放行,另外还需要回程方向——多数策略已经用 established,related 规则覆盖了它。
iptables -A FORWARD -i eth0 -o tun0 -p tcp --dport 29443 -m conntrack --ctstate NEW -j ACCEPT
iptables -A FORWARD -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
在添加任何东西之前,先检查 FORWARD 链的策略,因为早期 DROP 的顺序决定了你的规则究竟会不会被查询。在过滤逻辑位于 nftables 的主机上,把等价规则加到 inet 表的 forward 钩子上,并确认基础链确实看到了被转发的流量,而不只是本地输入。如果新规则上的数据包计数保持为零而 NAT 计数在上升,说明数据包在你的规则之前就被丢弃了。这是去读链顺序的信号,而不是再加一份同样规则的信号。
从外部网络证明它
从无关网络进行测试。来自同一局域网另一台机器的探测可能经过 hairpin NAT 支持,从未走过远端客户端实际使用的路径,所以那里的绿色结果证明的是错误的事情。
tcpdump -ni any port 29443
conntrack -L | grep 29443
在服务器和客户端上同时抓包。服务器上的抓包显示转换后的数据包是否朝隧道方向离开;客户端上的抓包显示请求是否到达、回应指向何处。conntrack -L 中的转换条目证明规则匹配过,而它的缺席则证明数据包从未到达 NAT 链。
端口扫描器是弱证据。应用绑定并监听之前,它们会报告端口关闭,所以要使用到真实服务的真实连接。当它终于可用时,把暴露面留在这一个端口上:因为第一条规则感觉别扭就转发 SSH 或管理面板,是比发布一项服务大得多的决定。