Azure P2S VPN 中的静态 IP:客户端地址为何会变
Azure 点到站点每次连接都从地址池里分配下一个空闲地址,因此客户端没有固定 IP。请改用网段或身份来控制访问。
每个 Azure 点到站点客户端在通过身份验证时会从地址池里拿到一个地址,并且只在这条连接存续期间保留它。既没有预留表,也没有按用户建立的映射,更没有哪项网关设置表示“永远把 172.16.0.10 给这个人”。正是这同一个机制解释了人们后来撞上的两种症状:一条 NSG 规则不再匹配它从前匹配的用户,以及一份允许清单悄悄放进了没人主动添加的机器。把规则换成更聪明的 IP 规则两种症状都治不了,因为规则所指名的东西,会按一张你并不掌控的时间表被重新分配。
地址池是容量,不是身份。它存在的意义是让并发隧道各自拿到唯一地址,并在租约结束的那一刻把它们回收。想通这一点之后,有用的问题就不再是“我怎么把这个客户端钉住”,而是“我能针对什么写规则,而网关不会把它回收”。
客户端连接时网关到底做了什么
网关持有配置好的客户端地址池,并把它当成一张租约表。连接时它取下一个空闲地址,实践中就是当前可用的最低地址,并把这个地址分配给隧道适配器,直到客户端断开或租约到期。决定结果是顺序而不是身份:在平静一段时间之后第一个重新连上的人,会拿到那一刻恰好空着的最低地址。
地址池的大小会掩盖这一行为。只比测试人数多一两个地址的实验池看起来是确定性的,因为同一台笔记本往往每天早上都按同样的顺序重新连接。加一台机器、改一下池的大小,或者让某个会话连着跨越午休,这个模式就会崩掉。地址从来都不稳定,稳定的是那支队伍。
点到站点配置的任何改动也会重置租约空间。换一种身份验证方式、改过大小的池、新的池网段,或者一座拆掉重建的网关,都会把客户端送回一个已经和它们旧地址对不上的池。那次改动里没有任何一处碰到你的 NSG,所以故障看起来像是自己冒出来的。
这件事大约一分钟就能看到。连上,在客户端的连接详情里记下隧道地址,断开,再连另一台机器,然后把第一台重新连上。你拿回来的地址取决于第二台机器占着什么,而没有任何门户设置能按需把上一个地址还给你。
基于客户端 IP 的 NSG 其实就是一条关于连接顺序的规则
人们最先写的规则很明显:在管理虚拟机的网卡或子网上放一条入站规则,把 VPN 客户端的内部地址当作源。它很短,写的那天就能通过测试,而只要池里不止一个空闲地址,它就会失效。
由此产生两种失效方式,其中只有一种看起来像缺陷。合法用户被拒绝,因为此刻正有别人占着规则所指名的地址,这件事以一张恼人的支持工单形式出现。或者,一个本不该来的用户被放了进来,因为他继承了一个规则仍然信任的地址,而这件事不会以任何形式出现,直到有人去审计访问权限。
由于规则跟着租约走,实际生效的策略读起来是“第一个连上的人获得管理访问权”。这是把连接顺序描述乔装成了意图,任何规则卫生都不会让它变成意图的表达。
客户端租约和网关地址是两回事
大部分混淆来自一个词。网关有一个相对稳定的公网 IP;客户端持有的池内私有地址并不稳定。对于没有区域冗余的 VPN 网关,公网 IP 是动态分配的,但它能扛过改大小、重置和例行维护——只有当网关本身被删除并重建时它才会变。区域冗余和区域级网关 SKU 使用标准 SKU 的公网 IP,而且那次分配必须是静态的。
本地对端在它自己的配置里需要的正是这份稳定性,而它对于识别某一个客户端毫无说明。当合作方问他们会看到哪个地址时,答案是站点到站点场景下网关的公网 IP,或者一个共享的 NAT 网段——绝不会是某台笔记本的隧道租约。
组池给你的是可以放心写规则的网段
虚拟 WAN 点到站点支持用户组,每个组可以被分配自己的客户端地址池。成员资格由身份决定,因此客户端落进哪个网段变得可预测:运维在一个网段,外部承包方在另一个网段,而网段是防火墙或 NSG 规则不必猜测就能指名的东西。
由此得到的规则集有一个可以审阅的形状:
- 每个受众一个池,按并发会话的峰值来定大小,而不是按人数;
- 组成员资格靠身份来管理,而不是每来一个人就去改规则;
- NSG 或 Azure 防火墙规则针对池网段来写,绝不针对单条租约;
- 对于永远不该共用同一网段的受众,给一个独立的中心或网关。
| Rule target | Survives a reconnect | What it still cannot express |
|---|---|---|
| Client tunnel address | No | Follows connection order and admits whoever holds the address next |
| Whole client pool | Yes | Grants every connected client identical access |
| Virtual WAN group pool | Yes | Only as reliable as group membership |
| Gateway public IP | Until the gateway is recreated | Identifies the site, not the person |
把整个池放进允许清单是诚实的,但很粗糙:那个网段里每个连上的客户端都拿到同样的访问权。这是一个值得大声说出来的策略决定,而不是在评审时被发现的疏漏。凡仍然需要按人回答的事情,都属于通过角色分配落在资源上的控制,而不属于包过滤。
当部署无法迁到虚拟 WAN 时
许多网关仍然是普通 VPN 网关,而迁到虚拟 WAN 并不是随手一改。务实的答案是:把按用户的判断从包过滤挪到本来就清楚用户是谁的那一层。
资源级身份是最便宜的一种。存储、SQL 和 Key Vault 都支持 Entra ID 身份验证并按用户或按组分配角色,因此 VPN 只需要证明网络可达。带具名账户的跳板机对管理类工作起到同样的作用,因为主机记录了人,隧道只负责搬运数据包。把客户端登记进 DNS 并放行名字而不是号码,等于把规则抬高一层。应用层身份——证书主体、令牌声明、组成员资格——在需要作出判断的是应用而不是网络时发挥作用。
对于坚持要固定源地址的合作方,答案是站点到站点或 ExpressRoute,并给出一个定义好的对端前缀。他们放行对端前缀,出现在他们那一侧的地址属于网关,不会因为有人重启笔记本而改变。
在改下一份允许清单之前,先检查地址池
地址池的两个特性造成了人们归咎于客户端的大部分行为。池太小会让租约回收得很快,于是地址看起来总在移动,任何基于地址的规则都从设计上就显得不可靠。池与本地网段或虚拟网络的地址空间重叠时,会造成路由上的含糊,而没有任何防火墙规则能修好它。
在碰任何规则之前,把三样东西放到同一页上:点到站点池、虚拟网络地址空间、本地网段。它们不能重叠,池要容得下每一个并发客户端,还要留出重新连接的余量。
然后按这个顺序把环闭上:
- 从两台机器按两种顺序分别连接并对比地址;如果它们可以互换,任何基于地址的规则都不安全。
- 检查网关是否为虚拟 WAN;如果是,请用组池而不是按地址写规则。
- 问一句:当规则命中了错误的人会发生什么;如果答案是一起访问事件,就把控制挪到身份上。
- 把哪个网段属于哪个组,记在和 NSG 定义同一个仓库里。
- 每次改动池、身份验证或网关之后都重新测试,因为这些改动会在不通知的情况下重写映射。