你已经连上了 AWS Client VPN 端点,私有子网能应答,但每个公网页面仍然超时。这个组合把问题缩小到受管侧的三类对象之一:客户端安装的路由、端点自身的路由及其授权规则,或者关联子网的下一跳。我按这个顺序检查,因为每种故障在客户端看来都不一样。本文只覆盖与 VPC 关联的受管端点,不涉及 EC2 实例上的 OpenVPN 服务器,也不涉及 Cloud WAN 或 Transit Gateway 连接。

先看客户端实际走的是哪条路由

已连接状态只证明 TLS 会话和客户端拿到了 CIDR,别的什么也证明不了。隧道开启时,问操作系统一个公网地址会往哪走。在 Linux 上,ip route get 1.1.1.1 应当指出隧道接口;在 macOS 上,netstat -rn -f inet | head -5 显示默认条目;在 Windows 上,Find-NetRoute -RemoteIPAddress 1.1.1.1 打印被选中的接口和下一跳。

接着读分离隧道开关,因为它决定端点发布的路由是不是客户端唯一送入隧道的目标:

aws ec2 describe-client-vpn-endpoints \
  --client-vpn-endpoint-ids cvpn-endpoint-0123456789abcdef0 \
  --query 'ClientVpnEndpoints[0].SplitTunnel'

当 SplitTunnel 为 true 时,公网目标只有在端点发布了覆盖它的路由时才会走隧道,所以缺少 0.0.0.0/0 会让这部分流量留在本地接口上。为 false 时客户端会接管流量,但端点仍必须为其做路由和授权,否则数据包进了隧道就停在那里。改动其中任一设置后都要重连客户端,因为路由和 DNS 服务器是在连接建立时下发的,运行中的客户端可能保留旧的一整套。

分开读端点路由和授权规则

这是两个对象,建了其中一个并不会建另一个。列出端点路由,找一条目标是关联子网的默认路由:

aws ec2 describe-client-vpn-routes \
  --client-vpn-endpoint-id cvpn-endpoint-0123456789abcdef0

每条路由恰好对应一个目标子网,所以三个关联各需要一条对应的默认路由。AWS 记录了这项覆盖要求,而缺口的实际表现是:用户从一个子网能工作,从另一个子网卡住,看起来像间歇性故障而不是少了路由。官方 Client VPN 场景文档 描述了这些路由所属的公网子网布局。

授权是第二个对象。当没有任何规则覆盖公网目标时,隧道会把数据包带进去,服务再把它丢掉,于是私有资源照常可用,公网却不行:

aws ec2 describe-client-vpn-authorization-rules \
  --client-vpn-endpoint-id cvpn-endpoint-0123456789abcdef0

针对 VPC CIDR 的规则并不等同于 0.0.0.0/0。补齐完整隧道所缺的部分,意味着每个关联子网一条路由,再加一条日后可审计的授权规则:

aws ec2 create-client-vpn-route \
  --client-vpn-endpoint-id cvpn-endpoint-0123456789abcdef0 \
  --destination-cidr-block 0.0.0.0/0 \
  --target-vpc-subnet-id subnet-0a1b2c3d4e5f67890
aws ec2 create-client-vpn-authorization-rule \
  --client-vpn-endpoint-id cvpn-endpoint-0123456789abcdef0 \
  --destination-cidr-block 0.0.0.0/0 \
  --access-all \
  --description "internet egress for full tunnel"

--access-all 会把目标授予该端点的所有客户端,所以只在隧道需要保持完整期间才留着它;有目录组可绑定时,绑定到组的规则更合适。

把数据包送出关联子网

端点路由决定的是 AWS 把数据包交给那个子网,并不决定之后的下一跳,那一跳在子网自己的 VPC 路由表里:

aws ec2 describe-route-tables \
  --filters Name=association.subnet-id,Values=subnet-0a1b2c3d4e5f67890 \
  --query 'RouteTables[].Routes[?DestinationCidrBlock==`0.0.0.0/0`]'

一条可用的默认路由会指向公网子网对应的互联网网关,或私有子网对应的 NAT 网关;状态为 blackhole 说明目标已被删除,这条路径现在悄无声息地断掉。这与端点路由列表是不同的表,修好一个并不修好另一个。

这一跳上还有两道过滤器。应用在目标网络关联上的安全组必须允许朝公网目标的出站流量,任何适用的网络 ACL 必须允许请求以及临时端口上的返回流量。关联安全组过于严格,是私有访问能用而公网访问不能用的常见原因。

按症状选择先查哪一层,不要三样一起改:

What you observeLayer to check first
No tunnel interface for a public IPClient route or split-tunnel flag
Tunnel interface chosen, request times outEndpoint route and authorization rule
Endpoint route present, still times outSubnet route table, association security group
Named host fails, numeric address worksDNS servers and their own authorization

把 DNS 故障和路由死掉分开

域名解析的失败方式和转发不同,而完整隧道让这个区别变得重要,因为被推送的解析器现在要经隧道才能到达。如果端点指定了 VPC 的 DNS 服务器,它们的地址同样需要路由覆盖和授权。直接问解析器,看属于哪种失败:dig +short example.com @10.0.0.2,或者 nslookup example.com 10.0.0.2 在 Windows 上。

当数字 URL 能通、带名字的不通时,先停止改默认路由,去处理 DNS。发往 IP 地址的请求并不能证明带名字的虚拟主机及其证书没问题,所以最终核验里要留一个带名字的测试。

时间也能帮忙。几秒内没有回应的 TCP 连接指向路由或过滤器,而卡在域名查询里的请求指向解析器。在解析器不可达时去改默认路由,往往把一个故障变成两个。

干净地回滚,然后核验出口路径

两个对象都是追加式的,所以回滚是删除而不是修复:

aws ec2 delete-client-vpn-route \
  --client-vpn-endpoint-id cvpn-endpoint-0123456789abcdef0 \
  --destination-cidr-block 0.0.0.0/0 \
  --target-vpc-subnet-id subnet-0a1b2c3d4e5f67890
aws ec2 delete-client-vpn-authorization-rule \
  --client-vpn-endpoint-id cvpn-endpoint-0123456789abcdef0 \
  --destination-cidr-block 0.0.0.0/0 \
  --access-all

重连后确认流量走的是哪条路。把观测到的公网地址与 NAT 网关的弹性 IP 地址、或你本来想用的互联网网关做比较,并确认同一会话里私有资源仍然能应答。

最后给一份短清单,并把最后一项当作变更的一部分而不是文档:

  • 每个关联子网一条 0.0.0.0/0 端点路由。
  • 一条覆盖 0.0.0.0/0 的授权规则,只对你打算服务的客户端生效。
  • 每个关联子网上都有一条不是 blackhole 状态的 VPC 默认路由。
  • 关联安全组的出站规则,以及 ACL 的返回流量。
  • 解析器地址已路由并授权,并用带名字的请求验证过。
  • 为路由和规则记录一份回滚方案。

一点范围说明:这是 IPv4 路径。双栈客户端会保留它已有的 IPv6 连通性,所以 IPv6 默认路由是另一个决定,有自己的授权,而不是这次的副作用。

相关文章