为什么家庭 NAT 并不能保护你的 VPN 接口
家用路由器转发隧道,但判断解密后数据包的是主机。请分开阅读监听、内部路径与入站策略。
即使家里的路由器没有做端口转发,其后的笔记本电脑仍会接受经 VPN 到达的 SSH 连接。这并不矛盾,因为两台不同的设备在判断两个不同的数据包。路由器转发加密传输,而主机决定如何处理到达其隧道接口的解密数据包。把这两个判断分开,就是全部练习。
路由器转发隧道,主机判断数据包
在看任何一条规则之前,先画出从远端来源到服务的路径。外段是 VPN 传输,由路由器和网关处理。内段从端点剥去隧道层、主机收到发往其 VPN 地址的数据包时开始。
把这两段合在一起,路由器就会把主机所做的判断算作自己的功劳。端口转发页面或路由器日志只描述外部对话,二者都不说明主机会接受哪些解密流量。主机对内部数据包执行自己的入站策略,与对任何到达接口的数据包一样。暴露问题真正得到裁决的地方,正是这份策略。
通配监听不是公共服务
在 Linux 上,绑定 INADDR_ANY 意味着所有本地地址,因此服务可以在它从未命名的隧道接口上应答。IP 套接字参考描述了这种绑定,在从监听列表得出结论之前值得一读。通配条目是关于套接字的事实,而不是关于可达性的陈述。
下面两行是同一服务在两种不同绑定下的样子,差别就是接口是否重要这一问题的全部答案。先读套接字坐在哪个地址上。
ss -lntp "sport = :22"
LISTEN 0 4096 0.0.0.0:22 0.0.0.0:* users:(("sshd",pid=812,fd=3))
LISTEN 0 4096 10.8.0.6:22 0.0.0.0:* users:(("sshd",pid=812,fd=4))
说出真正可能到达该端口的布局
三种布局会把数据包放到那个接口上,而它们属于不同的负责人。说对是哪一种,才能决定该由谁改策略。
| Arrangement | What reaches the port | Where the decision lives |
|---|---|---|
| Another permitted VPN peer | A packet from the same tunnel subnet | Gateway forwarding policy and host input rules |
| A deliberate public forward | Traffic the gateway publishes on purpose | Gateway service definition |
| A subnet routed behind the gateway | Client traffic from the far side | Gateway route and host input rules |
浏览器显示的公共出口地址对这三种布局一无所知,它描述的是出站流量如何离开,而不是入站流量被允许什么。请改为从监听和日志里的源地址倒推。如果源是另一个隧道对端,网关策略才是关键文档。如果是转换过的地址,先找到转换再改规则。
把服务限定在你想要的那条路径上
如果 SSH 只需要从一条管理路径访问,就把那条路径写成源地址范围加接口。然后按接口、目的地址和源地址而不是按网络名称来限定主机入站规则,因为看起来像内网的名字可能代表任何地址。给下一位排障的人留一条路由。
nft add rule inet filter input iifname "tun0" ip saddr 10.8.0.0/24 tcp dport 22 accept
nft list chain inet filter input
一条写明隧道接口和管理范围的规则,把一个意图说在一处。拿它跟一条为 Wi-Fi 网络写的规则相比,后者对解密后的隧道流量只字不提。当两层不一致时,数据包遇到的是主机策略。
登录和补丁就留在原处;能到达某个端口,从来不等同于被允许使用它。记录任何陌生来源的地址族和时间,在归咎于某个功能或漏洞之前先调查。放行每个隧道对端的规则,与放行一个管理子网的规则是不同设计,也没有哪行日志会告诉你原本想的是哪一种。
用一个成功和一个拒绝来验收修复
两半都要证明:一个被批准的连接成功,一条被排除的路径被拒绝。拒绝是人们常跳过的那一半,而它恰恰说明规则在起作用,而不是站在一条放行的默认值旁边。要从被排除的一侧去测,而不是空想。
重连之后再重复这一对,因为以另一个接口名回来的隧道可能悄悄绕过限定在旧接口上的规则。真正重要的边界,是主机对实际收到的数据包所执行的那一条。