管理控制台中的批准看起来是最后一步,其实不然。最先超时的是应用,之后你才会发现路由器在线、路由已批准。批准是一个分发步骤,不是权限,也不是转发保证。数据包要到达子网路由器后面的主机,四个环节必须依次成立,而每一个都可能在前一个成功时失败。按顺序排查,并在第一个不成立的环节停下。

四个环节,一处失败

路由器通告前缀,tailnet 批准该通告,客户端接受收到的路由,访问策略允许这条具体连接。这是三个不同组件做出的四个独立决定。因此,已批准的路由只说明网络现在进入了 tailnet 的路由表。

第四个环节最常被跳过,因为前三个会留下可见状态。覆盖路由器自身 tailnet 地址的授权,并不自动覆盖它后面的私有子网。这是两个不同的目的地,而策略是按目的地评估的。

在 Linux 上,第三个环节还有一个让人意外的默认值:接受子网路由是可选项。多个图形客户端默认接受通告的路由,因此混合环境中可能出现一台机器失败、其余正常的情况。

应在失败的那台机器上确认设置,而不是假设平台。能正常工作的机器上路由从来不是缺失的那一环,就让它们证明 tailnet 侧是健康的。Tailscale 的子网路由器文档涵盖批准、客户端路由接受和策略环节。

在客户端运行的地方检查路由

从失败的客户端开始,而不是从路由器开始。确认路由存在,并且客户端设置允许接受路由。

tailscale status
tailscale debug prefs | grep -i route
ip route get 10.20.0.42

第一条命令显示对等节点列表以及子网路由器是否可达,第二条打印客户端偏好,包括路由接受标志,第三条询问内核:到子网上真实主机时会走哪个接口。如果最后一条命令给出的是物理接口而不是 Tailscale 接口,说明路由从未进入路由表。路由缺失是客户端侧的问题,不是批准问题。先修好它,再去看策略。

要在 Linux 上接受路由,请带上该标志重新连接,而不是假设它已设置:tailscale up --accept-routes。用其他标志加入的客户端会一直保留这些标志直到被修改,所以添加新标志前先检查当前设置。修改后重新运行第一条命令,路由应当出现在路由表中。

验证转发与回程路径

批准并不会让路由器转发数据包,也不会安排回程。两者都是路由器 LAN 侧的主机层面问题。要在路由器上测试,而不是在客户端上。

sysctl net.ipv4.ip_forward
iptables -t nat -L POSTROUTING -n -v
tcpdump -ni any host 10.20.0.42 and port 443

第一条命令必须报告 1,否则内核会丢弃被转发的数据包。第二条显示子网路由的伪装是否改写了源地址,这是默认行为,通常也是你想要的。第三条显示请求是否到达目的地、是否有数据返回;没有回应的请求属于回程路径问题,而不是批准问题。修改其中任何一项之前,请把三个结果放在一起读。

如果伪装已被关闭,目标网络需要一条回到 tailnet 地址的路由,中间的每一跳都必须知道它。重新开启伪装对单个服务来说是最快的修复,代价是目标看不到客户端的原始地址。当目标为了审计而记录源地址时,这个取舍很重要,因此要有意识地决定,而不是在故障处理中随手翻转标志。请记录你做了哪种选择,因为下一个人只会看到两种行为之一。

阅读针对该资源的访问规则

路由已存在、转发也已验证之后,剩下的怀疑对象就是策略。阅读那条本应允许连接的授权,并检查它的目的地,而不只是源。客户端自身的身份也必须匹配策略选择器,因此要在失败的那台机器上采集。

tailscale status --json | head -40
tailscale ping <router-name-or-tailnet-ip>
ping -c 3 10.20.0.42

状态输出会列出客户端自身的地址以及它能看到的对等节点,策略的源选择器必须与之一致。tailscale ping 证明的是到子网路由器本身的 tailnet 路径;它解析对等节点名称和 tailnet 地址,而不是被通告子网内部的主机。第三条命令通过子网路由发送普通 ICMP 回显,因此其结果描述的是目的地而不是路由器。tailnet 层面的拒绝通常以拒绝的形式返回,而出站后永不返回的数据包会超时。

名称失败与地址失败是两个不同的问题;在归咎于路由之前,先直接解析主机。授权要按资源的实际需求来写,而不是整个子网。为了一个应用能工作就打开整个 /24,正是狭窄需求变成宽泛授权的路径,之后没人敢把它撤销。

如果目标是访问互联网而不是私有子网,正确的组件是出口节点,而不是子网路由器,策略和路由也相应不同。扩大授权之前先确认这个决定。出口节点会改变默认路由;子网路由器只是通告一个前缀。

相关文章