为什么 VPN 出口后面的端口扫描结果会变化?
更换出口,端口可能在服务未移动的情况下给出不同回答。把路径观测与你真正需要的清单分开。
从办公室线路和 VPN 出口运行同样的端口检查,两个答案可能在一个没人改动过的地址上互相矛盾。这个差距来自路径以及究竟由哪台主机作答,而不来自服务是否在运行。三个问题把这些解释分开:探测实际问了什么、哪台主机回答、以及你自己的服务器记录了什么。按这个顺序走一遍,并在进入下一个问题前把每个答案写下来。
先固定探测,再比较两次运行
只有当两个状态词出自同一个实验时,它们才可比较。五个字段把这件事固定下来:远端看到的源地址、解析出的目的地址、端口、扫描类型和时间。改动其中任何一项,你做的就是第二个实验,而不是把第一个实验测了两遍。
先检查来源,因为 VPN 客户端可能开着,而流量仍然从物理接口出去。问远端看到了什么,然后问内核选了哪个接口:
curl -s https://ifconfig.me
ip route get 203.0.113.20不同网络上的两个地址意味着解释已经在手,其余检查只会确认它。当两者一致时,带着原因标志记录探测,让状态返回时绑定一个解释,而不是孤零零地作为一个光秃秃的词。扫描类型也属于这份记录,因为它描述的是问了什么,而不是回来了什么。没有来源、没有方法、没有时间戳的状态无法与任何东西比较:
nmap -sT -p 443,8443 --reason 203.0.113.20把该输出保存在记录旁边,而不是放在下一个人看不到的滚动缓冲区里。两次诚实的扫描互相矛盾,只有在没人写下条件时才算争论。条件固定之后,要么结果一致、问题关闭,要么结果不同,而差异是关于路径的事实。
弄清是哪台主机完成了握手
一次完成的握手证明有某台主机完成了它,而那台主机不一定是你想到的那个进程。负载均衡器、反向代理或 SYN 代理可能在真实服务器参与之前就作答,这是正常设计而非症状。对期望名称有效的证书也无法缩小范围,因为证书描述的是服务关系,并不说明哪台机器终止了连接。
Nmap 的端口状态参考把这当作基础:状态记录的是探测从其发送位置能观察到什么,而不是端口的属性。这样读状态词,两次原本矛盾的运行就开始显得一致而不是相反,问题也从端口是否开放转向谁站在它前面。
在后台而不是从外部回答第二个问题。接受连接却到不了上游的前端,恰好制造出服务健康但应用缓慢的外部图景。只有上游自己的记录能区分这两种情况:
tail -n 5 /var/log/nginx/access.log
ss -ltnp 'sport = :443'日志行显示应用实际看到的客户端地址,第二条命令显示持有 443 端口的是你的应用还是它前面的某个东西。如果两者对哪个地址连入各执一词,你看到的就是同一个请求的两条腿,工单应当这么写。
在归咎隧道之前检查你自己的规则
有意设置的策略是端口在一个网络上应答、在另一个网络上不应答的最常见原因,也是现有解释中最无趣的一个。访问规则的存在就是为了放行一个来源、丢弃其余来源,所以出口变化正是它们被写出来的那个事件。因此通过 VPN 得到不同的状态,并不说明提供商会检查流量。它说明你的源地址现在不同了,这是一个小得多的主张。
这些规则在服务器上可读,所以去读它们,而不是猜测网络的中间部分。列出提到两个地址段中任何一个的过滤规则,然后检查连接究竟有没有到达守护进程:
nft list ruleset | grep -n -e 203.0.113 -e 198.51.100
journalctl -u sshd -n 50 | grep 203.0.113第二个结果为空是有信息量的,因为没有任何东西到达守护进程,问题属于某个过滤器、安全组或路径本身。命中意味着服务看到了你的来源并选择了它的回答,而这个决定通常写在你能读到的规则里。两个结果都不支持关于提供商的故事,而且两者都比抓包更省事。
分别读取传输、证书和响应
三层产生三种观察,而测试其中之一的探测无法报告另外两层。把它们作为记录中各自独立的行,而不是折成一个结论,因为每一行都以自己的方式失败,而失败方式会告诉你下一步该看哪里。
| Observation | What it settles | What it leaves open |
|---|---|---|
| Connection completed | A host on the path accepted the handshake | Which host, and whether its upstream is reachable |
| Certificate valid for the name | The endpoint holds a credential for that name | Whether the process behind it is the one you meant |
| Expected response body | The application handled the request | Anything about other ports or other paths |
| Connection refused | A host replied and nothing was listening | Whether a filter sits ahead of the refused port |
| Timeout | No reply arrived before the deadline | Drop, blackhole route or an overloaded host |
一条命令会对你控制的服务产生前三行。把它的输出放在扫描旁边的记录里,而不是聊天消息里:
curl -v --resolve office.example:443:203.0.113.20 https://office.example/按顺序读取输出:握手回答传输问题,证书主体回答名称问题,响应体回答应用问题。当握手行成功而响应行不成功时,你面对的是上游损坏的前端,而不是被封锁的端口。那是与你原先开的工单不同的另一张工单,值得直说。
最后把四个事实写进工单:源地址、扫描类型、作答的主机和时间。后续比较所需的一切都活在这四项里,而下一个接手案件的人不会拥有你的终端历史。如果这四项填不全,这次运行就没有完成。