AWS Client VPN 已连接却上不了网:路由排查
AWS Client VPN 已连接但没有网络?请按顺序检查客户端路由、端点授权、子网出口和 DNS。
你已经连上了 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 observe | Layer to check first |
|---|---|
| No tunnel interface for a public IP | Client route or split-tunnel flag |
| Tunnel interface chosen, request times out | Endpoint route and authorization rule |
| Endpoint route present, still times out | Subnet route table, association security group |
| Named host fails, numeric address works | DNS 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 默认路由是另一个决定,有自己的授权,而不是这次的副作用。