SSD Nodes Learn 🎉 VPS $4.99/月起
指南 Matt Connor作者: Matt Connor

如何在 VPS 上运行 Tailscale 子网路由器

在 VPS 上将私有网段发布到 tailnet,涵盖路由审批、重启后仍生效的 IP 转发,以及 Linux 必须设置的 --accept-routes 标志。

Tailscale 子网路由器的作用

Tailscale 子网路由器是一台向 tailnet 广播整个私有 IP 地址范围的机器,因此 tailnet 中的每台设备都可以访问该范围内的地址,即使这些地址对应的设备没有运行 Tailscale。tailnet 是您的私有 Tailscale 网络,指登录到同一账户或组织的一组设备。人们常将其与出口节点混淆,但出口节点执行的是相反的任务。它会将设备的所有流量经由 VPS 发出,因此 VPS 会成为该设备访问公网的路由。

每句话说明一个要点。子网路由器使一个私有网络可从 tailnet 访问。出口节点改变公网流量的出口位置。如果您需要的是后者,请改阅如何在 VPS 上运行 Tailscale 出口节点。它们是不同的标志,一个 VPS 可以同时承担两种角色,但两者解决的问题不同,故障方式也不同。

需要子网路由器的 VPS

常见情况是,服务提供商已经为您分配了一个私有网络。您的 VPS 有一个公网地址,以及位于私有网段上的第二个网络接口;该网段中的其他服务器都没有公网地址,例如位于 10.0.0.20 的数据库和位于 10.0.0.30 的备份目标。将 Tailscale 部署到一台 VPS 上,通告 10.0.0.0/24,您的笔记本电脑就可以直接访问这些私有地址。该网段中的其他配置无需更改,数据库仍然没有公网地址。

另一种情况是,网络位于 VPS 的另一侧。例如,某个路由器后面的家庭或办公室 LAN(局域网),或者一组完全无法运行 Tailscale 的设备,例如受管交换机或固件受限的旧 NAS。该网络中的一台 Linux 主机将充当整个网络的子网路由器。

这两种情况都有一个共同要求。子网路由器必须已经能够使用自身的路由表和防火墙访问它所通告的网段。Tailscale 不会建立这条连接。它只负责将流量传送到路由器,再交给内核转发。

先安装 Tailscale,并先检查本地路由

curl -fsSL https://tailscale.com/install.sh | sh

该脚本会检测发行版、添加 Tailscale 软件包仓库、安装 tailscale 命令和 tailscaled 守护进程,然后启用服务。使用 systemctl is-active tailscaled 确认安装结果;该命令应输出 active

在执行其他操作前,先确认 VPS 可以访问您计划发布的网络。

ip route show
ping -c3 10.0.0.20

ip route show 必须在实际网络接口上列出该私有地址段,例如 10.0.0.0/24 dev enp7s0 proto kernel scope link src 10.0.0.5。如果在这里 ping 失败,也就是无法访问路由器本身,任何 Tailscale 参数都无法解决问题。问题出在 VPS 的网络配置或目标主机上的防火墙。请先修复该问题,因为后续每项测试都依赖网络连通性。

启用 IP 转发,并让设置在重启后保留

除非已启用转发,否则 Linux 计算机会丢弃所有目标地址不是本机的数据包。转发其他计算机的数据包是子网路由器的核心职责,因此此步骤不可省略。

echo 'net.ipv4.ip_forward = 1' | sudo tee -a /etc/sysctl.d/99-tailscale.conf
echo 'net.ipv6.conf.all.forwarding = 1' | sudo tee -a /etc/sysctl.d/99-tailscale.conf
sudo sysctl -p /etc/sysctl.d/99-tailscale.conf

使用 sysctl net.ipv4.ip_forward 检查,命令应输出 net.ipv4.ip_forward = 1

很多人只能正确完成此步骤的一半。sudo sysctl -w net.ipv4.ip_forward=1 会立即生效,但下次启动时就会丢失。因此,子网路由器可能连续运行数周,却在内核升级后的重启次日停止工作。令人困惑的是,表面上看不到任何故障。tailscale status 仍会显示节点在线,管理控制台仍会显示路由已批准,客户端也仍安装着该路由。数据包到达 VPS 后,内核会直接丢弃它们,且不会记录任何日志。将这些值写入 /etc/sysctl.d/99-tailscale.conf,才能让设置在重启后恢复。

如果在转发仍未启用时发布路由,tailscale up 会在当时发出警告,并显示一行类似 Warning: IPv4 forwarding is disabled. Subnet routes and exit nodes may not work correctly. 的内容。请阅读该命令的输出,不要直接跳过。

发布路由

sudo tailscale up --advertise-routes=10.0.0.0/24

在已登录 tailnet 的 VPS 上,直接修改现有设置:

sudo tailscale set --advertise-routes=10.0.0.0/24

后续每次修改都使用 tailscale set。使用单个标志重新运行 tailscale up 会重置未重复指定的标志;CLI 会报错并提示,以这种方式修改设置时必须同时指定所有非默认标志。tailscale set 只修改一个设置,保留其他设置不变。

多个网段应放在同一个逗号分隔列表中,逗号后不要加空格:--advertise-routes=10.0.0.0/24,192.168.50.0/24。每个条目都必须使用 CIDR 表示法(无类别域间路由,即 10.0.0.0/24 格式)表示网络地址。若误填本机地址 10.0.0.5/24,系统会拒绝该值,因为前缀后的位不全为 0;错误信息会指出你可能想使用的前缀。若要停止发布路由,请使用 sudo tailscale set --advertise-routes= 设置空列表。

在管理控制台中批准路由

发布路由是一项请求,而不是变更。在管理员批准之前,没有客户端会收到该路由,地址段中的任何内容也都无法访问。这是有意设计的,因为任何能够将自身添加到所有人路由表中的机器,都可以劫持其任意地址段的流量。

在管理控制台的 Machines 页面中批准路由。VPS 会显示一个子网标记。打开对应行,找到子网部分,编辑路由设置,勾选该路由,然后保存。

批准按前缀分别生效。今天发布 10.0.0.0/24、下个月再发布 192.168.50.0/24 时,新前缀会处于未批准状态,而旧前缀仍可正常工作。对于 VPS,已批准的路由和被忽略的路由看起来完全相同,因此在排查其他问题之前,先检查控制台。

您可以在 tailnet policy 文件中使用 autoApprovers 块跳过手动操作:

{
  "autoApprovers": {
    "routes": {
      "10.0.0.0/24": ["tag:subnet-router"]
    }
  }
}

然后使用该标签启动节点,即 sudo tailscale up --advertise-routes=10.0.0.0/24 --advertise-tags=tag:subnet-router;路由发布后会立即获得批准。必须先在同一 policy 文件的 tagOwners 部分中创建该标签。如果您通过脚本重建 VPS,建议这样配置,因为重建后的节点会被视为新节点,其路由又会恢复为未批准状态。

为什么 Linux 客户端在未使用 --accept-routes 时会忽略路由

现在,路由已发布并获批准。您的手机和 Mac 可以访问 10.0.0.20。您的 Linux 笔记本电脑却无法访问,而且管理控制台中没有任何异常提示。

接受子网路由意味着将路由条目写入客户端的路由表。在 Android、iOS、macOS、tvOS 和 Windows 上,Tailscale 客户端会自动完成这项操作。在 Linux 上则不会,因为 Linux 计算机通常是服务器或路由器,其路由表往往是管理员有意配置的;如果系统静默插入从网络学习到的 /24,可能会中断该计算机已经处理的流量。因此,在 Linux 上,您必须在每个客户端上显式启用:

sudo tailscale set --accept-routes

然后检查路由写入了哪里:

ip route show table 52
ip route get 10.0.0.20

Linux 上的 Tailscale 不会将接受的路由放入主路由表,而是将其放入路由表 52,并安装策略规则。使用 ip rule show 可以看到优先级范围为 5210 到 5270 的这些规则;它们会将未匹配的数据包发送到该路由表。因此,单独运行 ip route show 永远不会列出 10.0.0.0/24,只检查该命令的读者会认为 --accept-routes 没有生效。ip route show table 52 才是显示实际结果的命令,其中应列出 tailscale0 上已发布的地址范围。

有一个例外需要注意。如果此 Linux 节点本身是其本地网络的第二个子网路由器,--accept-routes 会使它将发往自身直连子网的流量通过另一个路由器发送,而不是通过自己的接口发送。在高可用性对中的备用路由器上,应关闭 --accept-routes,只发布路由。

故障模式:两个路由器通告重叠的网段

两个子网路由器不得通告完全相同的网段。前缀长度不同的重叠网段是允许的,Tailscale 会选择最具体的匹配项。如果路由器 A 通告 10.0.0.0/24,路由器 B 通告 10.0.0.0/16,则发往 10.0.0.20 的流量会经过 A。

A 离线时,实际行为容易让人误解。Tailscale 不会回退到不那么具体的路由。发往 10.0.0.20 的流量会中断,而发往 10.1.0.20 的流量仍会通过 B 正常传输。表面上看,像是私有网络有一半不可用;实际原因是某个离线节点持有更具体的前缀。如果需要故障转移,请让通告更宽网段的路由器同时通告更窄的前缀,使两个路由器覆盖相同的地址。

另一种重叠发生在客户端附近。当您在 192.168.1.0/24 的酒店网络上,而子网路由器通告 192.168.1.0/24 时,两个路由会竞争相同的目标地址,最终使用哪个路由取决于平台。在 Linux 上,请在 Tailscale 自己的规则之前添加一条规则,使本地地址使用主路由表:

sudo ip rule add to 192.168.1.0/24 priority 2500 lookup main

该规则不会持久保存,下次启动时会消失。真正的解决方法是选择一个您在其他网络中不会遇到的私有地址范围。大多数家用路由器默认使用 192.168.0.0/24192.168.1.0/24,因此应有意选择 10.0.0.0/8 内的某个地址范围。出于相同原因,这种冲突也会破坏手动配置的普通 WireGuard VPN:更具体的本地路由会优先匹配,因此流量不会进入隧道。

故障模式:DNS 解析到没有路由覆盖的地址

这种问题很难调试,因为没有任何组件报告错误。名称可以解析,但连接会超时。

假设 db.internal.example.com 通过您的私有名称服务器解析到 10.0.5.20,而您发布了 10.0.0.0/24。查询会成功,因为 DNS(域名系统)解析和 IP 路由是两个独立步骤,彼此都不会检查对方。随后,发往 10.0.5.20 的数据包在 tailnet 上找不到匹配路由,于是通过客户端的默认网关离开并丢失。

使用以下两个命令可以分别检查这两个环节:

nslookup db.internal.example.com
ip route get 10.0.5.20

如果查询返回地址,但 ip route get 没有使用 dev tailscale0 响应,则说明名称解析正常,缺少的是路由。请发布能够覆盖该地址的范围,可以使用 10.0.0.0/16 或者再发布一个显式前缀,然后在控制台中批准新的前缀。

名称服务器本身也存在一个对应的陷阱。如果您在管理控制台中将全局名称服务器设置为类似 10.0.0.53 的私有地址,该地址必须位于已批准的路由范围内,否则设备根本无法访问解析器。若您在指定一个设备无法访问的解析器时,同时启用了覆盖本地 DNS 服务器的选项,tailnet 中的所有设备都会立即失去名称解析,包括一秒钟前还正常工作的设备。应先发布并批准通往解析器的路由,再修改 DNS 设置。如果您一直在处理隧道内的 DNS 问题,WireGuard 隧道中的 DNS 如何失效介绍了相同的机制,但不涉及上层的协调机制。

源 NAT 和站点到站点链路

默认情况下,子网路由器会将每个转发数据包的源地址改写为自己的私有地址。这就是 SNAT(源网络地址转换)。这样无需修改私有网络,响应也能正常返回:10.0.0.20中的数据库会向 VPS 响应,因为它已经知道如何访问 VPS。代价是,数据库看到的所有 tailnet 连接都来自 VPS,因此按源地址配置的防火墙规则和访问日志无法提供有效信息。

如果希望保留客户端真实的 tailnet 地址,可以在 Linux 上关闭此功能:

sudo tailscale set --snat-subnet-routes=false

此时,私有网络中的主机需要添加返回 100.64.0.0/10 的路由。100.64.0.0/10是 Tailscale 分配给设备的地址范围,该路由应指向子网路由器。没有这条返回路由,响应会发往默认网关,无法到达客户端,因此连接会在第一个数据包之后一直挂起。请在私有网络的网关上添加静态路由,或者保持 SNAT 启用。

站点到站点链路是指两个子网路由器同时执行此操作。每个路由器都公布自己的网络,并接受对方公布的网络:

sudo tailscale up --advertise-routes=10.0.0.0/24 --snat-subnet-routes=false --accept-routes

在另一台路由器上使用其自身地址范围运行对应命令。两个地址范围必须不同。如果大文件传输出现停滞,而 sshping工作正常,原因通常是 MSS(最大分段大小)问题。MSS 是 TCP 数据包所携带的数据块的最大大小。隧道开销会使转发数据包过大,无法通过中间的某个链路;调整 MSS 可以解决此问题:

sudo iptables -t mangle -A FORWARD -o tailscale0 -p tcp -m tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu

使用 iptables-persistent保存该规则,否则它会在下次启动时消失。

保持服务稳定运行的日常维护

截至 2026 年 8 月,节点密钥默认在 180 天后过期。子网路由器上的密钥过期后,节点会退出登录,所通告的整个网段都会无法访问,而且没有任何配置变更可以解释这一现象。请在管理控制台的 Machines 页面中为此计算机禁用密钥过期,并记录已完成此操作。

Tailscale 优先在对等节点之间建立直接连接。无法建立直接连接时,才会回退到其中继服务器。中继可以正常工作,但会增加延迟。带有公网地址的 VPS 配置最简单:允许入站 UDP 41641 后,大多数对等节点都能直接连接。如果防火墙由 ufw 管理,请参阅 VPS 实际需要的 ufw 规则,了解具体语法。

访问规则是另一部分。默认 tailnet 中,您的每台设备都可以访问其他设备,因此批准的路由可以直接生效。编写 ACL 策略后,规则的目标端必须指定私有网段,因为 10.0.0.20 不是 tailnet 地址,针对 tailnet IP 或标签编写的规则不会涵盖它。

最后,请决定是否要使用一个不由您运行的协调服务器。Tailscale 的控制平面是托管服务。您的密钥仍保存在自己的计算机上,但账户和策略文件存储在那里。运行 Headscale:自行托管的 Tailscale 控制服务器,可以将这些内容保留在您自己的 VPS 上,但需要自行维护。如果您仍在比较这种模式和手写配置,请参阅 WireGuard 与 Tailscale 的对比,了解协调层提供的功能及其代价。

FAQ

子网路由器和出口节点有什么区别?

子网路由器会通告一段私有地址范围,使 tailnet 设备能够访问未运行 Tailscale 的计算机。出口节点会通告自身为通往整个互联网的路由,使设备将所有流量通过该节点的公网地址发出。一台 VPS 可以同时承担这两种角色。它们使用不同的标志,即 --advertise-routes--advertise-exit-node,并且都需要在管理控制台中单独批准。

为什么我的 Linux 客户端忽略了通告的子网路由?

Linux 客户端不会自动接受子网路由,必须明确启用。请在客户端运行 sudo tailscale set --accept-routes。然后使用 ip route show table 52 检查,而不是 ip route show。Tailscale 会将已接受的路由安装到路由表 52,并通过策略规则访问这些路由。因此,主路由表不会列出它们,看起来像是缺少一条正常工作的路由。

子网在重启后停止工作。哪里出了问题?

最可能是 IP 转发。使用 sysctl -w 设置的值不会在重启后保留,因此应将其写入 /etc/sysctl.d/99-tailscale.conf,并使用 sysctl net.ipv4.ip_forward 确认。如果转发已启用,但该地址范围仍无法访问,请在管理控制台中检查该节点。节点密钥默认在 180 天后过期。子网路由器的密钥过期后,表现通常像网络故障,而不是账户问题。

两个子网路由器可以通告相同的地址范围吗?

不能通告完全相同的范围。前缀长度不同的重叠范围可以共存,并且最长匹配路由优先。故障转移需要谨慎处理:如果拥有更具体前缀的路由器下线,Tailscale 不会回退到范围更大的路由,因此该流量会停止。要实现真正的备用路由器,应让两台路由器都通告相同的具体前缀。

主机名可以解析,但连接超时。为什么?

DNS 解析和路由是两个独立步骤。主机名可能解析为一个没有任何已批准路由覆盖的地址,数据包随后会通过客户端的默认网关发出。请在客户端运行 ip route get <address>。如果结果不包含 dev tailscale0,请通告覆盖该地址的范围,并在管理控制台中批准新的前缀。