我的笔记本显示已连接。AWS Client VPN 客户端亮着绿灯,endpoint 记录了会话,而我仍然打不开私有子网中某台 EC2 实例上的 MongoDB 端口。表面上什么都没坏:证书有效,配置来自控制台,隧道也在传输流量。错在我,因为我一直把已连接读成已授权。

登录只回答一个问题:这个身份能否建立会话?之后的一切由另外一套规则决定,而那些规则从来看不到你的证书。通过认证的会话和到达的数据包是两种不同的成功,大多数 VPN 起来了但主机不可达的报告,就住在这两者之间的缝隙里。

下面这些检查是我现在使用的顺序。

已连接的会话究竟证明了什么

握手过程中,endpoint 会用自身的信任库校验客户端证书;如果配置了 SAML,则把浏览器重定向到你的身份提供方。这一步过了,客户端就会拿到 endpoint 客户端 CIDR 里的地址,控制台显示连接处于活动状态。整个交换过程中,没有任何一步问过你是否能到达 10.0.4.17 的 27017 端口。

对目标地址的许可,是 endpoint 上另一个独立对象。每条流都会去匹配授权规则,每条规则把一个目标 CIDR 和一个被授予该规则的组配在一起。当没有任何规则覆盖该目标时,endpoint 接受会话,然后把流量丢掉。没有横幅,没有重连循环,客户端界面上也没有任何文字说被拒绝。

把授权规则和资源比,而不是和 VPC 比

列出规则,并把它们放在实例真实拥有的地址旁边:

aws ec2 describe-client-vpn-authorization-rules \
  --client-vpn-endpoint-id cvpn-endpoint-0a1b2c3d4e5f6a7b8

输出里每条规则都显示目标 CIDR 和组 ID。里面藏着两种失败。规则可能授权的是 10.0.0.0/16,而实例位于对等连接的 VPC 的 10.1.0.0/16 上;对等路由是存在的,但没人给第二个网段写授权规则。或者规则指向的目录组里,用户在目录中是成员,却不是 endpoint 评估的那个组。

写覆盖目标的最窄规则,只有确实需要第二个 CIDR 时才加第二条。快速上手时写下的宽泛 0.0.0.0/0 规则确实能用,所以它一路活到生产,也把模型藏了起来。

数据包能不能走,由两张路由表决定

endpoint 路由把目标 CIDR 映射到 endpoint 关联的某个子网,这只是半程。子网自己的路由表仍然得知道怎么把包转发出去,而一旦目标位于对等连接、中转网关或可通过站点到站点隧道到达的本地网段之后,这一点就开始起作用。

aws ec2 describe-client-vpn-routes \
  --client-vpn-endpoint-id cvpn-endpoint-0a1b2c3d4e5f6a7b8

找一条目标与资源 CIDR 匹配、指向关联子网的路由。没有就加上。如果有,而主机仍然没有回应,缺口就在 VPC 这一侧,那就去和关联子网的路由表对比。

客户端有一项设置会在不破坏隧道的前提下破坏路由:含 route-nopull 的配置。这条指令让基于 OpenVPN 的客户端忽略服务端下发的路由,于是接口只带着 endpoint 自己的地址起来,目标干脆没有路由。用 Linux 上的 ip route show 或 macOS 上的 netstat -rn 检查是否存在经过 tun 接口的路由。如果没有,从导出的配置里去掉 route-nopull,重新连接,让下发的路由生效。

安全组看不到你的客户端地址

这一节我错了一整个下午。对于关联到子网的 IPv4 endpoint,服务会把客户端的源地址转换成该子网中 endpoint 网络接口的地址。到达实例的数据包并不携带客户端显示的那个地址,所以一条放行客户端地址池(比方说 172.16.0.0/22)的安全组规则,完全匹配不上任何东西。

放行端口要用 endpoint 的安全组,也就是挂在关联接口上的那个,而不是客户端 CIDR。引用组比硬编码地址更耐久,因为接口地址不归你控制。网络 ACL 是无状态的,所以凡是有自定义条目的地方,入站和出站两个方向都要放行。

同时用实例的私有地址去连。公网 IP 会把数据包送出 VPC,再从另一条路径、按另一套规则放回来,所以用公网地址做的测试对私有路径什么也说明不了。

在第一个失败处停下的隔离顺序

按顺序往下走,哪里失败就停在哪里。每一步排除的是一层,而不是全部。

  1. 确认会话用的是哪个身份,endpoint 评估的是哪个组。来自某个信任库的证书,和来自目录的 SAML 断言并不是同一个主体。
  2. 确认有授权规则覆盖该组对应的资源 CIDR。
  3. 确认存在 endpoint 路由并指向关联子网,再确认子网路由表能把包继续转发。
  4. 确认安全组放行的是来自 endpoint 接口的端口,而不是来自客户端地址池。
  5. 从 VPC 内部、用同一子网另一台实例上的 SSM 会话去测,从而把应用问题和客户端路径问题分开。

当这些步骤不能定论时,我退回到这张表:

SymptomLayer to inspect
Client shows connected, host times out, no byte growth on the interfaceAuthorization rule, then security group
Ping fails but a TCP connect to the port succeedsICMP is blocked; not an access failure
Tunnel reconnects every few minutesTwo machines sharing one client certificate
Works on one laptop, fails on anotherLocal profile has route-nopull or stale pushed routes

账号里有两个细节会产生看起来像权限失败的症状。每个用户都需要独立的客户端证书;一张证书两台机器共用,会话就会互相顶掉,结果是重连循环,而不是拒绝。新建 endpoint 时,控制台会显示一个 DNS 名称,某些客户端版本只接受带 endpoint 页面上那串随机前缀的完整写法,所以要复制全名,不要自己拼。

如果第五步卡住了,在改动任何东西之前,先把 endpoint 的连接日志拉下来,读其中的授权条目。日志会写明被检查的目标,以及匹配或未匹配的规则,把四层的问题压缩成一个答案。

相关文章