SSD Nodes Learn Hosting plans →
指南 Matt Connor作者: Matt Connor · 更新于 2026-11-01

如何在 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,您的笔记本电脑就可以直接访问这些私有地址。网段中的其他配置无需更改,数据库仍然没有公网地址。如果您只需要访问该网段中某台服务器的一个 Web 应用和一个端口,那么通告整个地址范围就超出了实际需求,此时可以改用 Tailscale serve 为该单个端口提供 HTTPS。同样的原理也适用于有意仅绑定到 localhost 的守护进程,例如在 systemd 下以无头模式运行的 dsh。此时,VPS 上的 tailnet 地址可以替代原本为了访问其 UI 而需要保持开启的 SSH 隧道。

另一种情况是 VPS 另一端的网络:位于自有路由器后面的家庭或办公室 LAN(局域网),或者一组完全无法运行 Tailscale 的设备,例如受管交换机或固件受限的旧 NAS。该网络中的一台 Linux 主机会成为整个网络的子网路由器。前面的家庭路由器无需为此配置端口转发。Tailscale 如何在不打开路由器端口的情况下穿透 NAT 解释了其工作原理,以及何时会回退到中继。家庭环境中,这台主机通常是运行在现有虚拟化主机上的小型 VM。决定服务应部署在隧道的哪一端之前,应先解决 家庭 Proxmox 主机与租用 VPS 的成本比较。

这两种情况有一个共同要求。子网路由器必须已经能够通过自身的路由表和防火墙访问它所通告的地址范围。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 失败(即在路由器本身执行 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 会立即发出警告。在 Linux 上,该提示接近 Warning: IP forwarding is disabled, subnet routing/exit nodes will not work.。请读取该命令的输出,不要直接跳过。

发布路由

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 策略文件中使用 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 启动节点,路由在发布时就会立即获得批准。该标签必须先存在于同一个策略文件的 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.0.1.20 的流量仍会经由 B 正常工作,因为只有 B 的 /16 覆盖该地址。其表现像是私有网络的一部分发生故障,实际原因是一个离线节点仍占用着更具体的前缀。如果需要故障转移,请让范围更大的路由器也通告较窄的前缀,使两个路由器覆盖相同的地址。

另一种重叠发生在更靠近客户端的位置。当你在 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/24 和 192.168.1.0/24,因此应在 10.0.0.0/8 中有意选择一个地址范围。相同的冲突也会破坏手动配置的普通 WireGuard VPN,原因相同:更具体的本地路由会获胜,因此流量不会进入隧道。

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

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

假设通过私有 nameserver,db.internal.example.com 解析到 10.0.5.20,并且你通告了 10.0.0.0/24。查询会成功,因为 DNS(domain name system)解析和 IP 路由是两个独立步骤,二者都不会检查对方。随后,发往 10.0.5.20 的数据包在 tailnet 上找不到匹配路由,于是通过客户端的默认网关离开,并最终丢失。

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

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

如果查询返回了地址,但 ip route get 没有使用 dev tailscale0 响应,则说明名称配置正常,而此客户端缺少路由。在 Linux 客户端上,先确认已启用 --accept-routes,因为如果未启用,即使已有获批前缀覆盖该地址,也不会安装子网路由。如果已启用,但没有获批前缀覆盖该地址,请通告一个覆盖该地址的范围,可以使用 10.0.0.0/16,也可以添加第二个显式前缀,然后在控制台中批准新前缀。

nameserver 本身也存在对应的陷阱。如果你在管理控制台中将全局 nameserver 设置为 10.0.0.53 之类的私有地址,则该地址必须位于获批路由内,否则不在该私有网络上的设备完全无法访问解析器。若在指向无人可访问的解析器时启用覆盖本地 DNS 服务器的选项,tailnet 中的每台设备都会同时失去名称解析,包括前一秒还正常工作的设备。先通告并批准到解析器的路由,再修改 DNS 设置。如果你一直在排查隧道内的 DNS 问题,WireGuard 隧道中的 DNS 故障方式介绍了相同的机制,但不涉及上层的协调层。

源 NAT 和站点到站点连接

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

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

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

在另一台路由器上使用其自身地址范围运行对应命令。两个地址范围必须不同。如果大数据传输发生停顿,而 ssh 和 ping 正常,原因通常是 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 保存此规则,否则它会在下次启动时消失。

保持正常运行所需的维护

这台机器的密钥是否过期,取决于您登录时采用的方式。像上文 autoApprovers 示例中那样通过标签启动的节点,从首次添加标签并完成身份验证时起默认就已禁用密钥过期,因此无需在此处进行任何设置。以您的用户身份登录的节点遵循 tailnet 的密钥过期期限。新建 tailnet 的默认期限为 180 days;截至 October 2026,可将其设置为 1 到 180 days 之间的任意值。此类子网路由器的密钥过期后,与它之间的连接以及经由它的连接都会停止,整个已通告网段也会变得无法访问,而且其他位置不会出现任何能解释这一现象的配置变更。对于以用户身份登录的路由器,请在管理控制台 Machines 页面中该设备所在行末尾的菜单里禁用密钥过期,然后记录已完成此操作。

Tailscale 优先在对等节点之间建立直接连接;无法建立时,则回退到其中继服务器。中继可以正常工作,但会增加延迟。具有公网地址的 VPS 属于最简单的情况:允许入站 UDP 41641 后,大多数对等节点都能直接连接。当访问私网网段的流量比 VPS 链路应有的速度更慢时,检查客户端是否直接连接到子网路由器,还是通过中继连接是首先应排查的事项。如果防火墙由 ufw 管理,VPS 实际需要的 ufw 规则介绍了相应语法。

访问规则是另一部分。在默认 tailnet 中,您的每台设备都可以访问其他设备,因此已批准的路由无需额外配置即可工作。编写 ACL 策略后,规则的目标端必须覆盖私网网段,可以直接指定该网段,也可以使用 * 通配符。该通配符包含已批准的子网路由,因为 10.0.0.20 不是 tailnet 地址,针对 tailnet IP 或标签编写的规则不会覆盖它。

最后,决定是否接受一个不由您运行的协调服务器。Tailscale 的控制平面是托管服务。密钥保留在您的设备上,但账户和策略文件存储在那里。控制平面遭到入侵或身份登录凭据被窃取后,攻击者实际能够执行哪些操作,这才是您在允许它进入私有网络之前需要明确的问题;Tailscale 的信任模型说明了这条边界所在的位置。成本通常不是用户放弃它的主要原因,因为免费计划支持最多 6 个用户,每个用户可使用不限数量的自有设备。不过,以标签启动的子网路由器与使用您的账户登录的子网路由器,计费方式不同。超过这一范围后,费用按用户而不是设备计算。因此,在添加会导致超出免费额度的账户之前,最好先确认免费计划用尽后,家庭用户或 5 人团队实际需要支付多少费用。

运行Headscale,即自行托管的 Tailscale 控制服务器,可以将控制平面保留在您自己的 VPS 上,但您需要负责维护。对于同一问题,另一种选择是连 Tailscale 客户端也不使用。自行托管 NetBird VPN 服务器可以将协调层及其网状网络客户端放在一台由您控制的设备上。两者底层都使用 WireGuard,因此切换方案时会发生变化的部分,正是NetBird 与 Tailscale 的对比所讨论的内容:由谁运行控制平面、如何编写覆盖私有地址范围的访问规则,以及对等端无法直接连接时如何中继流量。

如果您仍在手动编写配置与这种模式之间进行选择,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>。如果 Linux 客户端的结果不包含 dev tailscale0,请先检查 --accept-routes 是否已启用。如果已启用,请公布覆盖该地址的范围,并在管理控制台中批准新的前缀。

这篇文章对您有帮助吗?