VPN 流中的客户端源端口与服务器监听端口
比较同一条隧道中的两个端口号:服务器固定的监听端口与客户端临时源端口,以及路由器转换如何在两者之间发挥作用。
同一段对话里会出现两个端口号,两者都没有错。服务器日志显示提供服务的一端监听端口,笔记本电脑显示发出数据报的套接字,而中间的路由器可能在外出时改写第二个号。比较对话的两端,比比较数字本身更有用。下面的比较把监听端口、客户端套接字和转换后的源地址分开,因为它们回答的是不同的问题。
描述相对两端的两个数字
服务器配置的端口是客户端拨号的目标。它由策略固定,出现在配置文件中,并且必须在服务器一侧开放。客户端的源端口在本地选择,通常来自临时端口范围,存在的意义是让回复有地方落地。它可能每次重连都改变,设计上也不依赖它保持稳定。
中间的路由器可能把这个源改写成自己的 'NAT' 映射。因此,在服务器上看到的编号往往与笔记本选择的编号毫无关系。
源地址编号在出口处哪里发生变化
举一个虚构的 IPv4 例子:客户端位于执行转换的路由器之后,服务器只有一个 'UDP' 监听端口,一个数据报同时穿过两者。表中列出每个端点以及它会发生什么,而不是在三个观察点重复播放数据包。
| Endpoint | What stays fixed | What changes on reconnect | Who owns it |
|---|---|---|---|
| Server listener | The service port and the address the profile names | Nothing; the listener does not follow the client | The server operator |
| Client socket | The destination it dials | The local source port, chosen per socket | The client, or its operating system |
| Translated source | Usually the public address of the router | The port, and sometimes the address after a network switch | The NAT device or the upstream carrier |
每一行的目标都不变,变化的只是源的标识。UDP NAT 的要求把内部地址和端口与外部映射区分开来,这正是第二行与第三行的差别。路由器可以保留客户端的源端口,也可以另选一个;应用不应假定它会被保留。
从主机本身读取三个端点
下面的列表来自三台不同的机器,合起来展示了整段对话。第一个在服务器上运行,第二个在客户端上运行,第三个在任何存在映射的地方运行。单条日志永远无法同时显示三者。
ss -lunp 'sport = :1194'
ss -unp | grep 1194
conntrack -L -p udp | grep 1194
第一个列表证明服务器绑定在服务端口上,并显示哪个进程拥有它。第二个显示客户端的本地套接字以及它拨号的目标。第三个显示路由器创建的映射,这也是转换后的源唯一出现的地方。按这个顺序阅读,两个编号就不再显得矛盾。
重连会改变什么,不会改变什么
有些路由器在源端口空闲时保留它,有些每次分配不同的端口。两种行为都正常,要求前者的应用会在后者上出错。重连或切换网络后映射发生变化,并不是服务器的故障:客户端打开新套接字,路由器分配它现有的资源,服务器在同一个监听端口上看到新的源。真正的故障是期待服务器接受发往任意客户端端口的流量。
社区版 OpenVPN 客户端通过 nobind 获得这一行为,它让操作系统选择本地端口;远端端口仍来自配置文件。显式绑定是刻意的选择,而不是协议要求。服务器继续提供一个端点,而客户端各自选择自己的本地身份。
在记录中分别标注各层
隧道内部还有一段自己的对话,有自己的端口。隧道承载的 HTTPS 请求目标是 TCP 443,而外层加密连接使用 UDP 1194,因此改变外层源端口不会影响内层服务。
即使在外层,协议也很重要。UDP 1194 与 TCP 1194 是不同的端点,'TCP' 监听端口不会仅仅因为编号相同就收到 UDP 数据报。记录部署时,请把服务器监听端口、客户端套接字和转换后的源标为三件不同的事,并为每一项注明传输协议。
然后让策略跟随角色。服务器在监听端口上打开预期的传输,同时仍然验证对端;客户端的防火墙放行自己出站交换的回复,而无需发布入站服务。无状态过滤器必须明确写出返回方向,这正是这一区分在整齐的图之外也重要的实际原因。